- 指针
- 什么是指针
- 指针简单上手
- 指针的分类
- 整形指针&字符指针
- void指针
- 数组指针
- 函数指针
- 指针的类型与定义的意义
指针是我们进阶C语言的“拦路虎”,因为他在小白看来其定义如此复杂多变,难以捉摸。可是大神或者有一定变成经验的人来看,就是一个简答的概念。我们只要记住:指针即是变量又是地址。就像光具有波粒二象性一样。你可以把指针理解成为地址(方便理解),但是其本质还是一个变量只不过这个变量是专门存放地址的。
指针简单上手int a = 100;
printf("%d", a);
int* p = &a;//这个p就是存放a的地址的指针变量,任何指针变量在定一起类型的时候要加一个‘*’声明其是一个指针变量。
*p = 20;
printf("%d", a);//通过找到地址可以更改这的地址所指的内容。
指针的分类
整形指针&字符指针
这两个是比较常见和容易理解的指针,依次用int和char表示,他们的区别在于指向变量类型不同,内存也不一样,在进行解引用 *** 作时访问的字节大小也因为变量类型的区别会有所差异。整型指针可以访问4个字节,而字符指针只能访问1个字节。也就是说对整型指针变量解引用,一次可以 *** 作一个整型,而对字符变量解引用一次只能 *** 作一个字符。
较为特殊的char* p="hello"这并不是将整个字符串的地址传个了p,而是传了字符穿首元素‘h’的地址,可以通过’h‘的地址来找到整个字符串。此时出现char*p2=“hello”,p2和p代表的是同一处地址,因为hello是常量字符串,没有必要开辟两块不同的空间的来存储它。这是字符指针的一个特性。
- 整型指针 - int* - 指向整型的指针
字符指针 - char * - 指向字符的指针
int a = 100;
char ch = 'm';
char* pc = &ch;
printf("%d\n", a);
printf("%c\n", ch);
int* p = &a;
*p = 20;
*pc = 's';
printf("%d\n", a);
printf("%c\n", ch);
void指针
void型的指针可以接受任何类型的地址,但是不能对void型指针进行解引用 *** 作。解引用 *** 作要有特定的访问字节的数量,比如对整型指针解引用就是访问4个字节,字符型指针解引用就是访问1个字节,而void型指针无法确定访问字节个数,所以不能进行解引用 *** 作。同时void这种类型的指针也不能进行加减整数的 *** 作,因为无法确定跳过的字节个数。
典型的例子可以举一个 memmove函数。它可以对任意类型的数组变量进行粘贴,且可以出现重叠。
void * memmove ( void * destination, const void * source, size_t num );
在memmove的声明中,出现了void * 这个类型的变量,memmove可以对任意类型进行处理,因为可以通过void类型转化为想要的类型。定义子有很多,不会像strncpy只可以针对字符串。
/* memmove example */
#include
#include
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
数组指针
这是一种指向数组的指针,例如int(p)[10]这就是一个指向数组的指针,它指向的数组有10个元素,每个元素都是整型。给p加上括号是因为p和[10]优先结合,这样的话就变成了一个数组而不是指针了。这个数组叫 指针数组 ,int*p[10]这样的写法意思是一个有10个元素的数组,每一个元素都是整型指针,这和数组指针是两个不同的东西。
指向数组的指针里面存放的便是数组的地址,而非数组某个元素的地址,所以在定义数组指针时要用 &+数组名,而不是简单使用 数组名。
//关于数组名
//数组名是数组首元素的地址
//但是有2个例外:
//1. sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
//2. &数组名 - 数组名也表示整个数组,取出的是整个数组的地址
//除了这个2个例外,你见到的所有的数组名都表示首元素的地址
数组指针:指向数组的指针
char arr[] = "abcdef";
char* pa = arr;
printf("%s", pa);
但是我们还是要区分另外一个概念就是 指针数组,它是存放指针的数组。二者不要搞混了。
int a[] = { 1,2,3,4 };
int* p = a;
int (*pa)[4] = &a;//这个是 数组指针
printf("%p\n", p);
printf("%p\n", p+1);
printf("%p\n", pa);
printf("%p\n", pa+1);
char arr[] = "abcdef";
char arr2[] = "asdfgh";
char arr3[] = "qwerty";
char arr4[] = "zxcvbn";
char* pa[4] = { arr,arr2,arr3,arr4 };//这是 指针数组(多个函数也可以这样制作一个函数指针数组)
printf("%s\n", pa[0]);
printf("%s\n", pa[1]);
printf("%s\n", pa[2]);
printf("%s\n", pa[3]);
函数指针
函数指针顾名思义就是指向函数的指针,每个函数都有一个入口,这个入口的地址便是函数指针所指向的地址。函数地址的表示方法为 函数名或 &+函数名。例如一个函数叫Add,&Add和Add都是表示这个函数的地址没有什么差别。函数指针的写法是 函数的返回类型()(函数的参数),例如函数Add,其函数指针的写法就是int('‘p)(int,int)=Add 。’'p要加上括号来保证和p的优先结合来形成一个指针变量,如果不加括号来优先结合,则会出现int* p(int,int)这样的写法,这就变成了函数的声明,这个函数的返回类型是int*,函数的名字叫p,函数的参数是2个整型和原先的函数指针不是同一个意思。
用函数指针调用函数时可以不加*这个解引用符号,因为这个符号将不会在程序运行的时候起到作用。
//函数指针-也是指针-指向函数的指针
int Add(int x, int y)
{
return x + y;
}
int main()
{
//pf就是函数指针变量
int (*pf)(int, int) = Add;//&Add
//int sum = (*pf)(3, 5);
int sum = pf(3, 5);
printf("%d\n", sum);
return 0;
}
指针的类型与定义的意义
指针为什么要有类型呢?因为指针的类型决定了指针偏移的步长。
接下来给出一些案例来做具体分析。
//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//a在sizof()内代表整个数组,要计算整个数字大小 =4*4
printf("%d\n",sizeof(a+0));//a不是单独存在,代表a数组的首元素地址——&a[0],x86地址占4个字节,x64占8字节
printf("%d\n",sizeof(*a));//a不是单独存在,代表a数组的首元素地址——&a[0],*&a[0]=a[0].代表一个整形的大小=4
printf("%d\n",sizeof(a+1));//a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针的大小 - 4/8(4 or 8 下同)
printf("%d\n",sizeof(a[1]));//4——a[1]就是数组的第二个元素,sizeof(a[1])的大小
printf("%d\n",sizeof(&a));//&a是取了整个数组的地址,但还是地址——4/8
printf("%d\n",sizeof(*&a));//*&a=a 还是一整个数组的大小,16
printf("%d\n",sizeof(&a+1));//&a是取了整个数组的地址,再加一是往后移动16字节(a数组的大小)的下一个地址,还是地址 4/8
printf("%d\n",sizeof(&a[0]));//第一个元素的地址,地址就是4/8
printf("%d\n",sizeof(&a[0]+1));//第二个元素的地址 4/8
下一题
char arr[] = {'a','b','c','d','e','f'};//字符数组
printf("%d\n", sizeof(arr));//代表整个数组的大小=1*6
printf("%d\n", sizeof(arr+0));//代表的是&arr[0]+0,还是一个地址=4/8
printf("%d\n", sizeof(*arr));//等于*&arr[0]=arr[0] 为1
printf("%d\n", sizeof(arr[1]));// 等于1
printf("%d\n", sizeof(&arr));//4/8
printf("%d\n", sizeof(&arr+1));//跳到arr【】数组之后的地址,还是地址=4/8
printf("%d\n", sizeof(&arr[0]+1));//这个是arr[1]的地址,地址就是4/8
//一定要区分sizeof和strlen,前者是运算符,而后这是函数(函数传入地址,可以进行运算)
printf("%d\n", strlen(arr));//随机数,因为虽然strlen读取字符串的大小终止的符号是‘printf’如果见不到就会一直走
("%d\n",strlen (+arr0));//&arr[0]+0 还是一个地址,strlen继续往后读得到的还是随机数printf
("%d\n",strlen (*)arr);//*&arr[0]=arr[0]不是一个地址,无法计算,非法访问,errprintf
("%d\n",strlen ([arr1]));//arr[1]不是一个地址,无法计算,非法访问,errprintf
("%d\n",strlen (&)arr);//随机数,同第一个一样。printf
("%d\n",strlen (&+arr1));//随机数printf
("%d\n",strlen (&[arr0]+1));//随机数sizeof的案例结果:
strlen:出错的已经屏蔽掉,不然无法运行。
char
给出两个练习题,下期我公布答案并讲解。
[ arr]= "abcdef" ;printf
("%d\n",sizeof ()arr);printf
("%d\n",sizeof (+arr0));printf
("%d\n",sizeof (*)arr);printf
("%d\n",sizeof ([arr1]));printf
("%d\n",sizeof (&)arr);printf
("%d\n",sizeof (&+arr1));printf
("%d\n",sizeof (&[arr0]+1));printf
("%d\n",strlen ()arr);printf
("%d\n",strlen (+arr0));printf
("%d\n",strlen (*)arr);printf
("%d\n",strlen ([arr1]));printf
("%d\n",strlen (&)arr);printf
("%d\n",strlen (&+arr1));printf
("%d\n",strlen (&[arr0]+1));char
* =p "abcdef" ;printf
("%d\n",sizeof ()p);printf
("%d\n",sizeof (+p1));printf
("%d\n",sizeof (*)p);printf
("%d\n",sizeof ([p0]));printf
("%d\n",sizeof (&)p);printf
("%d\n",sizeof (&+p1));printf
("%d\n",sizeof (&[p0]+1));printf
("%d\n",strlen ()p);printf
("%d\n",strlen (+p1));printf
("%d\n",strlen (*)p);printf
("%d\n",strlen ([p0]));printf
("%d\n",strlen (&)p);printf
("%d\n",strlen (&+p1));printf
("%d\n",strlen (&[p0]+1));
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)