首先我们要知道的是,
定义指针变量必须要有*,如果没有*则成了int ptr 一个普通变量
char *a=“china”;不等价于 char *a;*a=“china”
char *a=“china”; //:表示=》定义一个char类型指针a,
注意,在定义的时候赋值,这其实是做了两个工作。
等同于char *a; //定义一个指针变量
定义好的指针变量是a, 而不是*a;
所以*a=“china”; (把字符串写入a的地址)是不对的,因为char *a;本来就是一个声明,还没有赋给a对应的地址。应该为a=“china”(把字符串的地址赋给a)
*符号,在定义的语句中,表示声明了一个指针的类型,
符号,在赋值语句中,表示一个运算,取这个变量的指向内容。
char *a="abcd";
a="bcde";
先把a指向"abcd"(地址a)然后再把a指向"bcde"(地址b)
char *a="abcd";
*a="bcde";
呢?
答案是不行,编译器不会报编译错误,但是在程序运行的时候就会出错。
程序运行到*a="bcde"; 时就卡住,然后非正常退出了。
也就是我们无法通过 向*a赋值一个字符串 这种方式来改变原本存有"abcd"的那一段内存中的内容。
本质上来说,char *s定义了一个char型的指针,它只知道所指向的内存单元,并不知道这个内存单元有多大,所以:
当char *s = “hello”;后,不能使用s[0]=‘a’;语句进行赋值。这是将提示内存不能为"written"。
当用char s[]=“hello”;后,完全可以使用s[0]=‘a’;进行赋值,这是常规的数组操作。
所以
char *s = (char *)malloc(n);//其中n为要开辟空间的大小
这句话其实相当于:
char s[n];
例子
#include <stdio.h>
int main(int argc, char* argv[]) {
char* buf1 = "this is a test";
char buf2[] = "this is a test";
printf("size of buf1: %d\n", sizeof(buf1));
printf("size of buf2: %d\n", sizeof(buf2));
return 0;
}
结果是:
$ > ./main
size of buf1: 4
size of buf2: 15
由此我们可以知道char*和char[]的区别在于每个小单元所储存的内容一个是整体string一个是单体char,而string是常量不可被更改
什么是指针:
指针是一种特殊的数据类型,使用它可以定义指针变量,指针变量存储的是整形数据,代表了内存的编号,通过这个编号可以访问对应的内存
为什么要用指针:(必须要用才用)
-
函数之间是共享变量
传参是单向值传递 全局变量容易命名冲突 使用数组麻烦、长度需要额外传递长度 虽然函数之间命名空间是独立的,但是地址空间是同一个,指针可以解决共享变量的问题 -
由于函数之间传参是单向的值传递(内存拷贝),对于字节数较多的变量,值传递的效率较低,如果指针传递变量的地址只需要
4|8个字节(提高函数传参效率) -
堆内存无法取名字,它不像data、bss、stack、让变量名与内存建立联系,只能使用指针记录堆内存的地址来访问对应的内存
int *ptr=array; 或int*a; a=array
- int array[20]={0};
- int *ptr=array;
- for(i=0;i<20;i++)
- {
- (*ptr)++;
- ptr++;
- }
这个例子将整型数组中各个坐标内存的值加1。由于每次循环都将指针ptr加1 个坐标,所以每次循环都能访问数组的下一个坐标
- char a[20];
- int *ptr=(int *)a; //强制类型转换并不会改变a 的类型
- ptr++;
指针ptr被加了1,它把指针ptr 的值加上了sizeof(int),int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以由a[0]的第四个字节到了a[4]的第四个字节。
- char a[20]="You_are_a_girl";
- int *ptr=(int *)a;
- ptr+=5;
在这个例子中,ptr 被加上了5,编译器是这样处理的:将指针ptr 的值加上5 乘sizeof(int),在32 位程序中就是加上了5 乘4=20。由于地址的单位是字节,故现在的ptr 所指向的地址比起加5 后的ptr 所指向的地址来说,向高地址方向移动了20 个字节。
- #include<stdio.h>
- int main()
- {
- char a[20]=" You_are_a_girl";
- char *p=a;
- char **ptr=&p;
- //printf("p=%d\n",p);
- //printf("ptr=%d\n",ptr);
- //printf("*ptr=%d\n",*ptr);
- printf("**ptr=%c\n",**ptr);
- ptr++;
- //printf("ptr=%d\n",ptr);
- //printf("*ptr=%d\n",*ptr);
- printf("**ptr=%c\n",**ptr);
- }
误区一、输出答案为Y 和o
误解:ptr 是一个char 的二级指针,当执行ptr++;时,会使指针加一个sizeof(char),所以输出如上结果,这个可能只是少部分人的结果.
误区二、输出答案为Y 和a误解:ptr 指向的是一个char *类型,当执行ptr++;时,会使指针加一个sizeof(char *)(有可能会有人认为这个值为1,那就会得到误区一的答案,这个值应该是4,参考前面内容), 即&p+4; 那进行一次取值运算不就指向数组中的第五个元素了吗?那输出的结果不就是数组中第五个元素了吗?答案是否定的.
正解: ptr 的类型是char **,指向的类型是一个char *类型,该指向的地址就是p的地址(&p),当执行ptr++;时,会使指针加一个sizeof(char*),即&p+4;那*(&p+4)指向哪呢,这个你去问上帝吧,或者他会告诉你在哪?所以最后的输出会是一个随机的值,或许是一个非法操作.
总结一下:
如果 ptr++; 改为*ptr++则为正确答案,流程分析:1.char *p=a; :由于a是一个数组,所以不需要用&a取地址,可以直接用a取地址,把a的地址赋给p,所以*p取地址内容也=Y 2. char **ptr=&p; 取p的地址赋给*ptr!!,所以*ptr,p,a其实是共用的一个地址
指针可以减指针但不可以加指针 (指针-指针)/指针类型宽度 计算两个指针间隔多少个指针元素
char**str可以约等于char*str[]都是可以储存字符串的,str里面储存的为指针(字符串的地址),指向的为字符串,所以str的类型是char**,由此可知*str指向的是字符串内的字符,所以str+1指第二个字符串,*(str+1)指第二个字符串的第一个字符,*(*(str+1)+1)指第二个字符串的第二个字符。数组来表示就是str[1]是第二个字符串,*str[1]是第二个字符串的第一个字符,*(str[1]+1)是第二个字符串的第二个字符
指针与const
当我们为了提高传参效率而使用指针作为函数参数时 ,传参效率提高了,但是变量被共享,存在被修改的风险,可以使用const保护指针所指向的内存
const int *p; 保护指针所指向的内存不被修改
int const *p; 同上
int *const p; 保护指针变量不被修改
const int *const p; 指针变量和指针所指向内存都不能修改
int const *const p; 同上
- char *str[3]={
- "Hello,thisisasample!",
- "Hi,goodmorning.",
- "Helloworld"
- };
- char s[80];
- strcpy(s,str[0]); //也可写成strcpy(s,*str);
- strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));
- strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));
上例中,str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char *。
*str 也是一个指针,它的类型是char *,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量.
str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'H'