Niejawna konwersja celu C traci precyzję liczb całkowitych „NSUInteger” (alias „unsigned long”) na ostrzeżenie „int”

187

Pracuję nad niektórymi ćwiczeniami i otrzymuję ostrzeżenie:

Niejawna konwersja traci dokładność liczb całkowitych: „NSUInteger” (aka „unsigned long”) na „int”

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{
    @autoreleasepool {

        NSArray *myColors;

        int i;
        int count;

        myColors = @[@"Red", @"Green", @"Blue", @"Yellow"];

        count = myColors.count; //  <<< issue warning here

        for (i = 0; i < count; i++)

        NSLog (@"Element %i = %@", i, [myColors objectAtIndex: i]);
    }

    return 0;
}

Zrzut ekranu

chłopiec małpa
źródło

Odpowiedzi:

470

countMetoda NSArrayzwraca NSUInteger, a na 64-bitowej platformie OS X

  • NSUIntegerjest zdefiniowany jako unsigned longi
  • unsigned long jest 64-bitową liczbą całkowitą bez znaku.
  • int jest 32-bitową liczbą całkowitą.

Więc intjest „mniejsza” niż typ danych NSUInteger, dlatego ostrzeżenie kompilatora.

Zobacz także NSUInteger w „Odniesieniu do podstawowych typów danych”:

Podczas budowania aplikacji 32-bitowych NSUInteger jest 32-bitową liczbą całkowitą bez znaku. 64-bitowa aplikacja traktuje NSUInteger jako 64-bitową liczbę całkowitą bez znaku.

Aby naprawić to ostrzeżenie kompilatora, możesz zadeklarować countzmienną lokalną jako

NSUInteger count;

lub (jeśli masz pewność, że tablica nigdy nie będzie zawierała więcej niż 2^31-1elementy!), dodaj jawną obsadę:

int count = (int)[myColors count];
Martin R.
źródło
19
Po prostu dodaję - głosowałem za odpowiedzią, gdy nagle otrzymałem mnóstwo ostrzeżeń i błędów w moim projekcie Xcode 5. Wspomniałeś o 64-bitowych, które skłoniły mnie do spojrzenia na moje ustawienia kompilacji. Xcode zmienił go na tryb 64-bitowy, który powodował błędy. Zmiana go z powrotem na arvm7 naprawiła wszystkie z nich.
Robert J. Clegg
1
@Tander czy istnieje różnica w wydajności między kompilacją 64-bitową a armv7?
Shaun Budhram
1
@ShaunBudhram Wygląda na to, że nie. Nie widziałem żadnej różnicy. Będzie to miało znaczenie tylko w aplikacjach, które intensywnie wykorzystują procesor - na przykład gry przyniosłyby korzyść z kompilacji dla wersji 64-bitowej.
Robert J. Clegg,
7
„Od 1 lutego 2015 r. Nowe aplikacje na iOS przesyłane do App Store muszą obsługiwać 64-bit ...” - Nowości i aktualizacje Apple Developer, 20 października 2014 r.
Pang
2
@JayprakashDubey: Apple nie widzi ostrzeżeń twojego kompilatora, ponieważ przesyłasz skompilowaną aplikację binarną do App Store. Dlatego aplikacji nie można odrzucić z powodu ostrzeżeń kompilatora. Oczywiście powinieneś je naprawić, aby aplikacja działała poprawnie.
Martin R
24

W przeciwieństwie do odpowiedzi Martina, rzutowanie na int (lub ignorowanie ostrzeżenia) nie zawsze jest bezpieczne, nawet jeśli wiesz, że twoja tablica nie zawiera więcej niż 2 ^ 31-1 elementów. Nie podczas kompilacji dla wersji 64-bitowej.

Na przykład:

NSArray *array = @[@"a", @"b", @"c"];

int i = (int) [array indexOfObject:@"d"];
// indexOfObject returned NSNotFound, which is NSIntegerMax, which is LONG_MAX in 64 bit.
// We cast this to int and got -1.
// But -1 != NSNotFound. Trouble ahead!

if (i == NSNotFound) {
    // thought we'd get here, but we don't
    NSLog(@"it's not here");
}
else {
    // this is what actually happens
    NSLog(@"it's here: %d", i);

    // **** crash horribly ****
    NSLog(@"the object is %@", array[i]);
}
Adrian
źródło
6
Masz rację, że rzutowanie wyniku indexOfObject:byłoby złym pomysłem. Moja odpowiedź dotyczyła określonego kodu w pytaniu i countmetoda nie może zwrócić NSNotFound. Nie zalecałem generowania ostrzeżeń int lub ignorowania ostrzeżeń. Przepraszam, jeśli to nie było jasne. W rzeczywistości Twój przykładowy kod wygenerowałby ostrzeżenie o, if (i == NSNotFound)gdyby został skompilowany dla wersji 64-bitowej, więc problem nie zostałby niezauważony.
Martin R
@Adrian: Jeśli nie masz nic przeciwko, co sugerujesz pytającemu?
moonman239
@ moonman239 Zasadniczo użyłbym zmiennej poprawnego typu, jeśli to możliwe (pierwsza sugestia @ MartinR), zamiast rzucania (jego druga). Jak podkreśla, casting jest bezpieczny w tym konkretnym przypadku, ale myślę, że to zły nawyk, ponieważ może mieć nieoczekiwane konsekwencje (jak w moim przykładzie). Napisałem, ponieważ ugryzła mnie ta szczególna sytuacja (chociaż jest to dobra uwaga na temat ostrzeżenia kompilatora ==, które musiałem przeoczyć).
Adrian
2
myślę, że liczenie będzie używane znacznie częściej niż indexOfObject, a wzdęcie pętli for za pomocą NSInteger po prostu brak „złego” stylu kodowania jest nonsensem. Powinieneś uważać wyłącznie na indexOfObject i upewnić się, że używasz tam NSIntegers, wszystko, co po prostu liczy coś, jest w porządku jako int, szczególnie w zakresie metod
NikkyD
5

Zmień klucz w Projekcie> Ustawienia kompilacji „ typecheck wywołuje printf / scanf : NIE

Objaśnienie: [Jak to działa]

Sprawdź wywołania printf i scanf itp., Aby upewnić się, że dostarczone argumenty mają typy odpowiednie dla określonego ciągu formatu i że konwersje określone w ciągu formatu mają sens.

Mam nadzieję, że to zadziała

Inne ostrzeżenie

cel c niejawna konwersja traci dokładność liczb całkowitych „NSUInteger” (alias „unsigned long”) na „int

Zmień klucz „ niejawna konwersja na typ 32-bitowy> Debugowanie> Architektura * 64: Nie

[ Uwaga: może unieważnić inne ostrzeżenie o konwersji architektury 64-bitowej] .

Darshit Shah
źródło
jeśli chcesz po prostu przekonwertować bibliotekę 32-bitową na 64-bitową, jest to obiecująca opcja.
San
2

Wykonanie rzutowania na „int” rozwiązuje problem w moim przypadku. Miałem ten sam problem. Więc:

int count = (int)[myColors count];
Vladimir Despotovic
źródło
czy nazywa się to „niejawne”? LOL :) W obu przypadkach działało.
Vladimir Despotovic