1. sizeof與strlen的對比
1.1 sizeof
sizeof屬于是操作符,用于計算變量所占的空間大小,單位為字節。如果操作數是類型的話,計算的是使用類型創建的變量所占內存空間的大小。
sizeof只計算數據在內存中所占的空間大小,而不在乎內存中存放的數據類型。
例如:
int main()
{int a = 10;printf("%d\n",sizeof a);printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));return 0;
}
4
4
4
1.2 strlen
strlen是C語言庫函數,其功能為求字符串長度。函數原型如下:
size_t strlen ( const char * str );
strlen函數會統計指針str指向的那個地址向后直到字符 '\0' 之前的所有字符個數。
但是,strlen函數會一直向后尋找直到找到 '\0' 為止,所以說有越界訪問的可能性。
int main()
{char arr1[] = "abd";char arr2[] = {'a','b','c'};printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));return 0;
}
3
6
這里在創建第二個字符串時沒有加上 '\0' ,所以第二行輸出的是一個隨機結果。像arr1這樣創建字符串,編譯器會自動在末尾加上一個 '\0' ,所以第一行的輸出結果是確定的。
1.3?sizeof 和strlen的對比
sizeof
1.sizeof是操作符
2.sizeof計算操作數所占內存的大小,單位是字節
3.不關注內存中存放什么數據
strlen
1. strlen是庫函數,使用需要包含頭文件 <string.h>
2.strlen用于求字符串長度,統計字符串中字符 '\0' 之前字符的個數
3.關注內存中是否存在字符 '\0' ,如果不存在,就會一直向后尋找,可能會越界。
2. 數組和指針題目解析
2.1 一維數組
int main()
{int a[] = {1,2,3,4};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a+0));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(a[1]));printf("%d\n",sizeof(&a));printf("%d\n",sizeof(*&a));printf("%d\n",sizeof(&a+1));printf("%d\n",sizeof(&a[0]));printf("%d\n",sizeof(&a[0]+1));return 0;
}
下面為解析:
int main()
{int a[] = {1,2,3,4};printf("%d\n",sizeof(a)); //sizeof中a單獨出現,表示整個數組,結果為16printf("%d\n",sizeof(a+0));//(a+0),這里a表示數組首元素地址,整體屬于整型指針,結果為4/8printf("%d\n",sizeof(*a));//(*a),這里的a表示數組首元素地址,*a表示首元素,類型為整型,結果為4printf("%d\n",sizeof(a+1));//(a+1),a表示數組首元素地址,與整數1相加,整體屬于整型指針,結果為4/8printf("%d\n",sizeof(a[1]));//a[1],a表示數組首元素地址,利用操作符[1]來訪問數組中下標為1的元素,類型為整型,結果為4printf("%d\n",sizeof(&a));//&a,得到的是整個數組的地址,類型為int(*)[4],結果為4/8printf("%d\n",sizeof(*&a));//(*&a),操作符*與&抵消,最終為a,表示整個數組,結果為16printf("%d\n",sizeof(&a+1));//&a+1,&a得到整個數組地址,+1后跳過整個數組,但是其結果類型依舊為 int(*)[4],結果為4/8printf("%d\n",sizeof(&a[0]));//&a[0],a[0]訪問數組中下標為0的元素,即首元素,操作符&得到該元素的地址,類型為整型指針,結果為4/8printf("%d\n",sizeof(&a[0]+1));//&a[0]+1,&a[0]得到數組首元素地址,+1后跳過一個元素,結果類型為整型指針,結果為4/8return 0;
}
16
8
4
8
4
8
16
8
8
8
2.2 字符數組
2.2.1 字符串末尾無 '\0'
代碼1:
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));return 0;
}
解析:
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));//szieof中數組名arr單獨出現,表示整個數組,結果為6printf("%d\n", sizeof(arr+0));//arr+0,表示數組首元素地址,類型為字符指針,結果為4/8printf("%d\n", sizeof(*arr));//*arr,arr表示數組首元素地址,*arr表示首元素,類型為字符,結果為1printf("%d\n", sizeof(arr[1]));//arr[1],表示數組中下標為1的元素,類型為字符,結果為1printf("%d\n", sizeof(&arr));//&arr,&arr得到整個數組的地址,類型為 char(*)[6],結果為4/8printf("%d\n", sizeof(&arr+1));//&arr+1,&arr得到整個數組的地址,+1跳過整個數組,但結果類型依舊為為char(*)[6],結果為4/8printf("%d\n", sizeof(&arr[0]+1));//&arr[0]+1,&arr[0]得到首元素的地址,+1后向后跳過一個元素,結果類型為字符指針,結果為4/8return 0;
}
6
8
1
1
8
8
8
代碼2:
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}
解析:
int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", strlen(arr));//arr表示數組首元素地址,但是數組中沒有字符'\0',strlen函數會從arr指向的地址向后一直尋找直到找到'\0',結果為隨機值printf("%d\n", strlen(arr+0));//arr表示首元素地址,+0后依舊為首元素地址,結果同第一行結果printf("%d\n", strlen(*arr));//arr為首元素地址,*arr得到首元素字符a,被傳遞給函數后,a會被轉化為其ASCII碼值97,并且函數會將97作為地址處理,但是這個地址通常是無法訪問的,因此編譯時會報錯,運行會導致程序崩潰printf("%d\n", strlen(arr[1]));//arr[1],為數組中下標為1的元素,即為b,其結果同第三行printf("%d\n", strlen(&arr));//&arr,&arr得到整個數組的地址,strlen函數會從arr指向的地址向后一直尋找直到找到'\0',結果為隨機值printf("%d\n", strlen(&arr+1));//&arr+1,&arr得到整個數組的地址,+1后跳過整個數組,strlen函數向后一直尋找直到找到'\0',結果為隨機值 printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1,&arr[0]得到數組首元素的地址,+1跳過一個元素,strlen函數向后一直尋找直到找到'\0',結果為隨機值 return 0;
}
將編譯錯誤的語句注釋之后得到如下的打印結果:
11
11
11
5
10
2.2.2?字符串末尾存在?'\0'
代碼3:
int main()
{char arr[] = "abcdef";printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));return 0;
}
解析:
int main()
{char arr[] = "abcdef";//這樣創建字符串時,編譯器為自動在字符串末尾加上一個'\0'printf("%d\n", sizeof(arr));//sizeof中數組名arr單獨出現,表示整個數組,數組中一共有"abcdef"以及末尾的'\0',類型都為字符,因此結果為7//其他的打印不受影響,不過&arr的類型變為了char(*)[7],結果與上面一致,printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));return 0;
}
7
8
1
1
8
8
8
代碼4:
int main()
{char arr[] = "abcdef";printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}
解析:
int main()
{char arr[] = "abcdef";//這樣創建字符串,編譯器會自動在字符串末尾加上'\0'printf("%d\n", strlen(arr));//arr,arr表示數組首元素,字符串末尾中有'\0',結果為6printf("%d\n", strlen(arr+0));//arr+0,arr表示數組首元素,+0后依舊為數組首元素,字符串末尾中有'\0',結果為6printf("%d\n", strlen(*arr));//編譯器報錯,原因同代碼2printf("%d\n", strlen(arr[1]));//編譯器報錯,原因同代碼2printf("%d\n", strlen(&arr));//&arr,&arr得到整個數組的地址,字符串末尾中有'\0',結果為6printf("%d\n", strlen(&arr+1));//&arr+1,&arr得到整個數組的地址,+1后跳過整個數組,strlen函數向后一直尋找直到找到'\0',結果為隨機值printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1,&arr[0]得到數組首元素的地址,+1后跳過一個元素,字符串末尾存在'\0',結果為5return 0;
}
將報錯語句注釋掉后的打印結果:
6
6
6
5
5
這里,倒數二行的輸出結果為5屬于是巧合,將字符串中的字符刪去兩個后,其結果如下:
int main()
{char arr[] = "abcf";printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}
4
4
4
5
3
可以發現,除了倒數第二行打印結果不變外,其余打印結果都發生了變化,并且剛好是原本結果減去2。
2.2.3 用字符型指針存儲常量字符串地址
代碼5:
int main()
{char *p = "abcdef";printf("%d\n", sizeof(p));printf("%d\n", sizeof(p+1));printf("%d\n", sizeof(*p));printf("%d\n", sizeof(p[0]));printf("%d\n", sizeof(&p));printf("%d\n", sizeof(&p+1));printf("%d\n", sizeof(&p[0]+1));return 0;
}
解析:
int main()
{char *p = "abcdef";//將字符串首字符地址存儲在字符型指針p中printf("%d\n", sizeof(p));//p在sizeof中單獨使用,表示字符串首字符的地址,類型為字符型指針,結果為4/8printf("%d\n", sizeof(p+1));//p+1,p代表字符串首字符的地址,+1后跳過一個字符,結果類型依舊為字符型指針,結果為4/8printf("%d\n", sizeof(*p));//*p,p為字符串首字符地址,*p得到首字符,即為a,類型為字符,結果為1printf("%d\n", sizeof(p[0]));//p[0],編譯器處理p[0]時,轉化為*(p+0),結果同第三行printf("%d\n", sizeof(&p));//&p,得到指針p的地址,類型為char**,結果為4/8printf("%d\n", sizeof(&p+1));//&p,得到指針p的地址,+1后跳過一個char**類型的空間大小即跳過指針p的地址,指向存儲指針p處地址后的地址,結果類型依舊為char**,結果為4/8printf("%d\n", sizeof(&p[0]+1));//&p[0]+1,轉化為&*(p+0)+1,*與&抵消,因此結果為p+1,指向字符中第二個字符處,即為存儲字符b的地址,結果類型為char*,結果為4/8return 0;
}
8
8
1
1
8
8
8
代碼6:
int main()
{char *p = "abcdef";printf("%d\n", strlen(p));printf("%d\n", strlen(p+1));printf("%d\n", strlen(*p));printf("%d\n", strlen(p[0]));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p+1));printf("%d\n", strlen(&p[0]+1));return 0;
}
解析:
int main()
{char *p = "abcdef";//字符串末尾存在'\0'printf("%d\n", strlen(p));//p為字符串首字符地址,傳遞給函數strlen,結果為6printf("%d\n", strlen(p+1));//p+1,p為字符串首字符地址,+1后跳過一個字符,傳遞給函數strlen,結果為5printf("%d\n", strlen(*p));//編譯器報錯,結果同代碼2printf("%d\n", strlen(p[0]));//編譯器報錯,結果同代碼2printf("%d\n", strlen(&p));//&p,&p得到指針p的地址,地址未知,但肯定不與字符串中任意一個字符重疊,傳遞給函數strlen后,結果為隨機值printf("%d\n", strlen(&p+1));//&p,&p得到指針p的地址,+1后地址依舊未知,但肯定不與字符串中任意一個字符重疊,傳遞給函數strlen后,結果為隨機值printf("%d\n", strlen(&p[0]+1));//&p[0]+1,&p[0]得到字符串首字符地址,+1后跳過一個字符,傳遞給函數strlen,結果為5return 0;
}
6
5
0
5
5
這里的倒數第二行打印結果也屬于巧合,可以跳過修改字符串驗證:
int main()
{char *p = "abef";printf("%d\n", strlen(p));printf("%d\n", strlen(p+1));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p+1));printf("%d\n", strlen(&p[0]+1));return 0;
}
4
3
0
5
3
2.3 二維數組
代碼7:
int main()
{int a[3][4] = {0};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a[0][0]));printf("%d\n",sizeof(a[0]));printf("%d\n",sizeof(a[0]+1));printf("%d\n",sizeof(*(a[0]+1)));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(*(a+1)));printf("%d\n",sizeof(&a[0]+1));printf("%d\n",sizeof(*(&a[0]+1)));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a[3]));return 0;
}
解析:
int main()
{int a[3][4] = {0};//創建一個二維數組并將內部元素全部初始化為0printf("%d\n",sizeof(a));//sizeof中數組名a單獨出現,表示整個數組,結果為48printf("%d\n",sizeof(a[0][0]));//a[0][0],a[0][0]表示數組中首元素,類型為整型數據,結果為4printf("%d\n",sizeof(a[0]));//二維數組a中,當sizeof中a[0]單獨出現,表示數組中的整個第一行,結果為16printf("%d\n",sizeof(a[0]+1));//a[0]+1,此處a[0]表示數組第一行首元素地址,+1后跳過一個元素,最終結果類型為整型指針,打印結果為4/8printf("%d\n",sizeof(*(a[0]+1)));//*(a[0]+1),a[0]+1表示數組中第一行第二個元素的地址,加上*表示該地址處存儲的數據,該數據類型為整型,結果為4printf("%d\n",sizeof(a+1));//a+1,a代表二維數組首行的地址,+1后跳過一整行,表示數組中第二行的地址,結果為4/8printf("%d\n",sizeof(*(a+1)));//*(a+1),a+1表示數組中第二行的地址,加上*,得到整個第二行,結果為16printf("%d\n",sizeof(&a[0]+1));//&a[0]+1,&a[0]表示數組中整個第一行的地址,類型為int(*)[4],+1后跳過一整行,但數據類型依舊為int(*)[4],結果為4/8printf("%d\n",sizeof(*(&a[0]+1)));//&a[0]+1,&a[0]表示數組中整個第一行的地址,類型為int(*)[4],+1后跳過一整行,此時指向第二行地址,加上*后,得到整個第二行,結果為16printf("%d\n",sizeof(*a));//*a,a表示二維數組首行的地址,加上*后,表示整個數組首行,結果為16printf("%d\n",sizeof(a[3]));//a[3]表示數組整個第四行,雖然此時屬于數組訪問越界,但是編譯器依舊將其作為數組的行類型處理,結果為16return 0;
}
48
4
16
8
4
8
16
8
16
16
16
數組名的意義:
1.sizeof(數組名),這里的數組名表示整個數組,計算的是整個數組的大小。
2.?&數組名,這里的數組名表示整個數組,取出的是整個數組的地址。
3. 除此之外所有的數組名都表示首元素的地址。