contributed by < ofAlpaca
>
CSIE5006
Note
C99[6.2.5] Types are partitioned into object types (types that fully describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).
struct Strt ;
struct Strt x ; // incomplete type
char y[]; // incomplete type
void *
之謎void *
(pointer to void) 無法確定其大小。
#include <stdio.h>
int main() {
int intvar = 1;
int intarr[] = {1,2,3,4,5};
printf("address of intvar = %p\n", (void *)(&intvar));
printf("address of intvar - 1 = %p\n",(&intvar - 1));
printf("address of intvar + 1 = %p\n", (void *)(&intvar + 1));
printf("address of intarr[0] = %p\n", (void *)(&intarr));
printf("address of intarr[1] = %p\n", (void *)(&intarr + 1));
}
$ ./test
address of intvar = 0x7fff2a78552c
address of intvar - 1 = 0x7fff2a785528
address of intvar + 1 = 0x7fff2a785530
address of intarr[0] = 0x7fff2a785510
address of intarr[1] = 0x7fff2a785524
void *
與 char *
可以互換表示法。*(int32_t * const) (0x67a9) = 0xaa6;
ptrA
不會被改變到的原因是參數 p
只是個副本,而改變副本的值並不會影響到 ptrA
。
int B = 2;
void func(int *p) { p = &B; }
int main() {
int A = 1, C = 3;
int *ptrA = &A;
func(ptrA);
printf("%d\n", *ptrA); // print 1
return 0;
}
int B = 2;
void func(int **p) { *p = &B; }
int main() {
int A = 1, C = 3;
int *ptrA = &A;
func(&ptrA);
printf("%d\n", *ptrA);
return 0;
}
memcpy()
也是個方法。
int B = 2;
void func(int * p) {
memcpy(p,&B,sizeof(B));
}
int main() {
int A = 1, C = 3;
int *ptrA = &A;
func(ptrA);
printf("%d\n", *ptrA); // print 2
return 0;
}
func(int **p)
改為 func(int *p)
就會發生 core dump,但依照邏輯上來說是沒問題的。
int B = 2;
void func(int *p) { *p = &B; }
int main() {
int A = 1, C = 3;
int *ptrA = &A;
func(&ptrA);
printf("%d\n", *ptrA); // Core dump happens here
return 0;
}
*p
的型別是 int
,所以在 assign 時,只會 assign 4 個 bytes,進而導致後面要 *ptrA
時產生問題。
int **p
,那 *p
也會是 pointer type。(gdb) p &B
$5 = (int *) 0x555555755010 <B>
(gdb) p ptrA
$7 = (int *) 0x7fff55755010
^^^^^^^^ 只有 4 bytes 被 assign
extern char x[]
無法轉為 pointer。char x[10]
不能轉為 pointer ,但 char x[]
可以,因為它不是 object 而是 incomplete type 。func(char x[])
會被轉為func(char *x)
,但僅限於函式的參數, array argument 是不存在於C語言的。
sizeof(x)
會造成 bug ,因為 sizeof(x)
只會回傳 4 bytes (as char *x)。#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
。func(char x[][10])
== func(char (*x)[10]
,所以 x+1
會一次增加 40 bytes (as 10 int)。
int a[3];
*a = 5; // a[0] = 5
*(a+1) = 5; // a[1] = 5
x[i]
會被編譯器改寫為 *(x + i)
, x[i] == (*((x)+(i)))
。x[i]
、 *(x + i)
、 *(i + x)
、 i[x]
這四者是等價的。int x[i][j]
意思是 x 是有 i 個 object 的陣列,每個 object 都是有 j 個 int 的陣列。此作法稱為 array subscripting。char * newargv[];
的結構如下,提醒自己用:char* s[]
與 char **s
是相同的。char *s[]
與 char (*s)[]
是不同的,前者是 an array of pointer to char ,後者是 a pointer to an array 。 (根據 C99 [A.2.1] , []
是優先於 *
)int *ptr, value;
ptr 為 pointer to int , value 為 int ,此手法容易搞混,不宜使用。ptr + 1
時,每次 +1
的單位取決於 ptr 的 type 。double : 0x00000000 0x3ff00000 // double 長度為 8 bytes , 但 int 長度為 4 bytes。
(int *): 0x00000000 // 轉型後只能取到前 4 bytes
char *r = malloc(strlen(s) + strlen(t) + 1);
最後的+1為NULL的空間。memcpy()
、 malloc()
並不會自動配上最後的 terminated null byte ,在處理 char *
時要記得自己保留空間。strdup()
會使用 malloc()
配置足夠的記憶體空間來存放字串,也包含了最後的 terminated null byte ('\0')。 *(uint32 * const) (0x601020) = 0x601080
左手邊是 Lvalue,指的是 locator value ,並非 left 。
E
可以代表:
a
int a = 10;
int *E = &a;
Lvalue 是指除了 void
型別以外,有著 object type 或 incomplete type 的表達式。ex. obj , *ptr , ptr[i] , and ++x。
C99 [6.3.2.1] An lvalue is an expression with an object type or an incomplete type other than void;
有著回傳型別的 function designator 會被轉為 pointer to returning type 。 ex. int test();
會被轉為 pointer to function return int , 型別為 int (*)()
。就像陣列這兩者互換的關係,char a[]
與 char * a
。
C99 [6.3.2.1] A function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’.
若在 pointer to function 使用 deference operator(*) , 則會被轉為 function designator 。
此程式碼可以運作的原因是 put()
是個 function designator ,而 function designator 會被轉成 pointer to function ,在經過數次的 dereference 後仍然是 function designator,所以不影響結果。
int main() { return (********puts)("Hello"); }
char *s="Hello world"
此為string literal,和 char s[]="Hello world"
不同,前者存於read-only data section ,後者則是 stack 的 local variable。
char* func1(){
char *p = "Hellow";
return p;
}
char* func2(){
char p[] = "Hellow";
return p;
}
int main()
{
printf("%s\n",func1()); // print Hellow
printf("%s\n",func2()); // NULL
return 0;
}
strlen()
不能接受 null pointer ,會造成未定義的行為。
struct S {int a, b;};
struct S x = {1,2};
struct S y = {2,1};
int i = ++(int){1}; // i is initialized as 2
#define NULL ((void *)0) // 一般C編譯器常見的寫法
#define **offsetof**(st, m) **((size_t)&(((st *)0)->m))**
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
((st *)0)->m
意思是將 0 轉型為 pointer to st ,並指向 m 這個 member 的位址。在此獲得相對位置。sizeof
一樣都是 operator ,用於回傳資料的型別,但 typeof
是 gcc 的 extension。