标签:大小 set 设置 关系 div msu const关键字 lock 名称
Everything you need to know about pointers in C
假设你声明一个名为foo的变量。
这个变量占用一些内存。 在当前主流的Intel处理器上,它占用四个字节的内存(因为int是四个字节宽)。int foo;
int *foo_ptr = &foo;
foo_ptr被声明为指向int的指针。我们已经初始化它指向foo。
指针也有一个类型,顺便说一句。 它的类型是int。 因此,它是一个“int指针”(int指针)。 int **的类型是int *(它指向int的指针)。 指针对指针的使用称为多重间接。 更多关于这一点。
在单个声明中声明两个指针变量的明显方法是:
int* ptr_a, ptr_b;
可以把它看作一个基本类型(int),加上一个间接级别,用星号(ptr_b的值为0,ptr_a的值为1)表示。int *ptr_a;int ptr_b;
请注意,星号已移动。 它现在紧挨着词ptr_a。 关联的微妙含义。int *ptr_a, ptr_b;
绝对最清楚的是保持每个声明在其自己的线上,但是可以占用很多垂直空间。 只使用你自己的判断。int ptr_b, *ptr_a;
没有什么问题。int *ptr_a, *ptr_b;
这对任何东西都没有用,除了声明函数指针(稍后描述)。int ((not_a_pointer)), (*ptr_a), (((*ptr_b)));
现在,如何为这个指针指定一个int? 这个解决方案可能很明显:
这也是错误的。foo_ptr = 42;
在此声明中,取消引用运算符(前缀*,不要与乘法运算符混淆)查找存在于地址处的值。 (这被称为“加载”操作。)int bar = *foo_ptr;
*foo_ptr = 42; Sets foo to 42
(这被称为“存储”操作。)
这里是一个三int数组的声明:
注意,我们使用[]符号,因为我们声明一个数组。 int * array在这里是非法的;编译器不会接受我们为其分配{45,67,89}初始化器。int array[] = { 45, 67, 89 };
假设我们要打印出数组的所有三个元素。
int *array_ptr = array;printf(" first element: %i\n", *(array_ptr++));printf("second element: %i\n", *(array_ptr++));printf(" third element: %i\n", *array_ptr);
如果你不熟悉++操作符:它加1到一个变量,同变量+ = 1(记住,因为我们使用后缀表达式array_ptr ++,而不是前缀表达式++ array_ptr,表达式计算到array_ptr之前的值增加,而不是之后)。first element: 45second element: 67 third element: 89
好吧...刚刚发生了什么?printf("%i\n", array[0]);
好吧,你可能想到了。 但是这与指针有什么关系?45
int array[] = { 45, 67, 89 };int *array_ptr = &array[1];printf("%i\n", array_ptr[1]);
89
那可能会大脑弯曲一点。 这是一个图:
数组指向数组的第一个元素; array_ptr设置为&array [1],因此它指向数组的第二个元素。 因此,array_ptr [1]等价于array [2](array_ptr从数组的第二个元素开始,因此array_ptr的第二个元素是数组的第三个元素)。
struct foo {size_t size;char name[64];int answer_to_ultimate_question;unsigned shoe_size;};
块中的每个声明都称为成员。 联合也有成员,但是使用方式不同。 访问成员如下所示:
表达式my_foo.size访问my_foo的成员大小。struct foo my_foo;my_foo.size = sizeof(struct foo);
One way to do it(*foo_ptr).size = new_size;
但是有一个更好的方法,专门为此目的:指针到成员运算符。
Yummyfoo_ptr->size = new_size;
不幸的是,它并不看起来好多多间接。
Icky(*foo_ptr_ptr)->size = new_size; One way(**foo_ptr_ptr).size = new_size; or another
抱怨:Pascal做得更好。 它的dereference运算符是后缀^:
Yummyfoo_ptr_ptr^^.size := new_size;
(但抛开这个抱怨,C是一个更好的语言。)
int a = 3;int *b = &a;int **c = &b;int ***d = &c;
下面是这些指针的值如何相等:
因此,&运算符可以被认为是添加星号(增加指针级别,因为我称之为),和*, - >和[]运算符作为删除星号(减少指针水平)。
当涉及指针时,const关键字有点不同。 这两个声明是等效的:
const int *ptr_a;int const *ptr_a;
然而,这两个不是等价的:
int const *ptr_a;int *const ptr_b;
在第一个例子中,int(即* ptr_a)是const; 你不能做* ptr_a = 42。在第二个例子中,指针本身是const; 你可以改变* ptr_b很好,但你不能改变(使用指针算术,例如ptr_b ++)指针本身。
注意:所有这些的语法似乎有点异国情调。 它是。 它混淆了很多人,甚至C的骑士。 熊与我。
也可以取一个函数的地址。 并且,与数组类似,当使用它们的名称时,函数衰减到指针。 所以如果你想要的地址,说,strcpy,你可以说strcpy或&strcpy。 (&strcpy [0]不会工作,很明显的原因。)enum { str_length = 18U }; Remember the NUL terminator!char src[str_length] = "This is a string.", dst[str_length];strcpy(dst, src); The function call operator in action (notice the function pointer on the left side).
有一个特殊的语法用于声明类型为函数指针的变量。
请注意上面声明中* strcpy_ptr周围的括号。 这些从星号指示返回类型(char *)的星号指示变量的指针级别(* strcpy_ptr - 一个级别,指向函数的指针)。char *strcpy(char *dst, const char *src); 一个普通的函数声明,供参考char *(*strcpy_ptr)(char *dst, const char *src); Pointer to strcpy-like functionstrcpy_ptr = strcpy;strcpy_ptr = &strcpy; This works toostrcpy_ptr = &strcpy[0]; But not this
char *(*strcpy_ptr_noparams)(char *, const char *) = strcpy_ptr; Parameter names removed — still the same type
指向strcpy的指针的类型是char *(*)(char *,const char *); 你可能会注意到这是上面的声明,减去变量名。 你可以在转换中使用它。 例如:
strcpy_ptr = (char *(*)(char *dst, const char *src))my_strcpy;
正如你所期望的,指向函数的指针的指针在括号内有两个星号:
char *(**strcpy_ptr_ptr)(char *, const char *) = &strcpy_ptr;
我们可以有一个函数指针数组:
char *(*strcpies[3])(char *, const char *) = { strcpy, strcpy, strcpy };char *(*strcpies[])(char *, const char *) = { strcpy, strcpy, strcpy }; Array size is optional, same as everstrcpies[0](dst, src);
这是一个病理声明,取自C99标准。 “[这个声明]声明一个没有参数返回int的函数f,没有返回指向int的参数的参数指定的函数fip和一个没有返回int的参数指定的函数的pointerpfi”“(6.7.5.3 [ 16])
int f(void), *fip(), (*pfi)();
换句话说,上面的等价于以下三个声明:
int f(void);int *fip(); Function returning int pointerint (*pfi)(); Pointer to function returning int
但如果你认为这是心灵弯曲,支撑自己...
char *ptr;
这个声明告诉我们指针类型(char),指针级(*)和变量名(ptr)。 后两个可以进括号:
char (*ptr);
如果我们用名称后面跟一组参数替换第一个声明中的变量名,会发生什么?
嗯。 函数声明。char *strcpy(char *dst, const char *src);
一个函数指针变量!char *(*strcpy_ptr)(char *dst, const char *src);
请记住,指向不带参数并返回int的函数的指针的类型是int(*)(void)。 所以这个函数返回的类型是char *(*)(char *,const char *)(同样,inner *表示指针,outer *表示指向函数的返回类型的一部分) 。 你可能还记得这也是strcpy_ptr的类型。char *(*get_strcpy_ptr(void))(char *dst, const char *src);
strcpy_ptr = get_strcpy_ptr();
因为函数指针语法是如此令人费解,大多数开发人员使用typedef来抽象它们:
typedef char *(*strcpy_funcptr)(char *, const char *);strcpy_funcptr strcpy_ptr = strcpy;strcpy_funcptr get_strcpy_ptr(void);
此数组的长度为16个字节:“I am the Walrus”为15个字符,加上NUL(字节值为0)终止符。 换句话说,str [15](最后一个元素)是0.这是如何“信号”的结尾。char str[] = "I am the Walrus";
注意使用指针运算和取消引用。 这是因为,尽管函数的名字,这里没有“字符串”; 只有一个指向至少一个字符的指针,最后一个为0。size_t strlen(const char *str) { Note the pointer syntax heresize_t len = 0U;while(*(str++)) ++len;return len;}
size_t strlen(const char *str) {size_t i;for(i = 0U; str[i]; ++i);When the loop exits, i is the length of the stringreturn i;}
那一个使用索引。 其中,正如我们早先发现的,使用指针(不是数组,绝对不是字符串)。
标签:大小 set 设置 关系 div msu const关键字 lock 名称
原文地址:http://blog.csdn.net/sergeycao/article/details/53668722