- 指针是什么
指针是编程语言中的一个对象,利用地址,它的的值直接指向存在电脑存储器中的另一个地方的值。通过地址就能找到所需的变量单元。因此地址形象化的称为指针,通过它能找到以它为地址的内存单元。就像生活中住酒店一样,通过房间号就找到了你要住的的房间,这里的房间号就是地址,而房卡就是指针,房卡上面存有房间号。
指针是一个变量,存放内存单元的地址,存放在指针中的值都被当成地址处理。
- 一个单元的大小是一个字节,一个字节给一个对应的地址。
- 指针的大小在32位平台上是4个字节,在64位平台上是8个字节。
- 指针的类型
跟变量一样,指针也有类型,如整形,浮点型等。char* 类型的指针存放char类型变量的地址,short* 类型的指针存放short类型变量的地址,int*类型的指针存放int类型变量的地址。 - 指针±整数
#include<stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%p\n",&n);
printf("%p\n",pc);
printf("%p\n",pc+1);
printf("%p\n",pi+1);
return 0;
}
总结:指针的类型决定了指针向前或者向后走一步有多大的距离,一个char或int等。
指针的解引用,char的指针解引用只能访问一个字节,int 的指针解引用能访问四个字节。
- 指针和数组名
先看一个例子:
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n",arr);//数组首元素的地址
printf("%p\n",&arr);//数组首元素的地址
printf("%p\n",&arr[0]);//数组首元素的地址
printf("%p\n",&arr+1);//跳过一个数组大小的地址
return 0;
}
数组名和数组首元素的地址是一样的。数组名表示数组首元素的地址,因此可以使用指针来访问数组。
-
指针运算
-
指针±整数
-
指针-指针(不能指针+指针)
-
二级指针
指针变量是变量,是变量就有地址,指针变量的地址也存在指针中,这个指针就是二级指针。
int a = 10;
int* pa = &a;
int** ppa = &pa;
a的地址存放在pa中,pa的地址存放在ppa中,pa是一级指针,ppa是二级指针。*ppa通过对ppa中的地址进行解引用,这样找到的是pa,*ppa访问就是pa,然后对pa进行解引用找到a。
区分
1. 字符指针 2. 指针数组 3. 数组指针 4. 函数指针 5. 函数指针数组 6. 指向函数指针数组的指针
- 字符指针
字符指针是一种指针类型位字符型char*
#include<stdio.h>
int main()
{
char* ptr = "hello world";
printf("%s\n",ptr);
return 0;
}
ptr中存放的是hello world字符串的首地址,而不是存放字符串hello world,即一个常量字符串的首字符h的地址存放到指针变量ptr中。
注意:
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
char *str3 = "hello bit.";
char *str4 = "hello bit.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指 针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候 就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
- 指针数组
指针数组是一个存放指针的数组。
int* arr[10];//整形指针数组
char* arr2[10];//一级字符指针数组
- 数组指针
数组指针是指针,能指向数组的指针。
int *p1[10];
int (*p2)[10];
//p1指的是一个存有10个整形的指针数组,p2指的是一个指向有10个整形数组的指针
//p2先和*结合,说明p2是是一个指针变量,然后指向的是一个大小为10个整形的数组,所以p2是一个指针,指向一个数组,叫数组指针。
- 函数指针
void test()
{
printf("hello\n");
}
void (*pfun1)();
void *pfun2();
//pfun1能存test函数的地址
两段有趣的代码:
(*(void (*)())0)();
//解释:调用0地址处的函数,该函数的参数是无参,返回类型为void
void (*signal(int,void(*)(int)))(int);
//解释 :signal是一个函数声明,函数的参数为int和一个函数指针,该函数指针指向的函数参数为int,返回类型为void;signal函数的返回类型也为函数指针,该函数指针指向的函数参数为int ,返回类型为void
- 函数指针数组
要把函数的地址存到一个数组中,这个数组就叫函数指针数组。
int (*parr1[10])();
//parr1先和[]结合,说明parr1是数组,数组的内容是int(*)()类型的函数指针;
int *parr2[10]();
int (*)() parr3[10];
- 指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针。
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[10])(const char*) = &pfunArr;
return 0;
}
指针和数组笔试题:
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()
{
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr)); //6
printf("%d\n", sizeof(arr+0)); //4
printf("%d\n", sizeof(*arr)); //1
printf("%d\n", sizeof(arr[1])); //1
printf("%d\n", sizeof(&arr)); //4
printf("%d\n", sizeof(&arr+1)); //4
printf("%d\n", sizeof(&arr[0] + 1));//4
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));//从'b'的地址开始的随机值
return 0;
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7
printf("%d\n", sizeof(arr + 0));//4
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4
printf("%d\n", sizeof(&arr + 1));//4
printf("%d\n", sizeof(&arr[0] + 1));//4
printf("%d\n", strlen(arr));//6
printf("%d\n", strlen(arr + 0)); //6
printf("%d\n", strlen(*arr));//错误
printf("%d\n", strlen(arr[1]));//错误
printf("%d\n", strlen(&arr)); //6
printf("%d\n", strlen(&arr + 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));//5
return 0;
}
int main()
{
char *p = "abcdef";
printf("%d\n", sizeof(p));// 4
printf("%d\n", sizeof(p + 1));//4
printf("%d\n", sizeof(*p)); //1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4
printf("%d\n", sizeof(&p + 1));//4
printf("%d\n", sizeof(&p[0] + 1));//4
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p + 1));//5
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));5
}
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;
}
总结:
1.数组名单独放在sizeof()内部,数组名表示整个数组,计算的是整个数组的大小。
2.&数组名,这里的数组名表示整个数组,取出的是数组的地址,&数组名+1,加的是整个数组的大小。sizeof(&数组名)是4个或8个字节。
3.除此之外所有的数组名都表示数组首元素的地址。
-
区别sizeof与strlen
-
sizeof:
1.计算所有变量类型占用内存的大小,单位是字节。
2.在计算字符串时,要加上字符串后面的’\0’的大小。
3.计算数组名的时候,计算的是整个数组的大小。 -
strlen
1.计算的是字符串的长度大小。
2.计算字符串长度时,以’\0’为结束标志,不计算’\0’的大小。 -
指针中常遇到的一些陷阱
1.指针遇到运算符*和++的情况:
*和++的优先级相同,都是自右向左的顺序结合。
a = *p++ //先将指针自增,然后再取指针的内容赋给a
a = (*p)++ //取指针的内容赋值给a,然后指针再自增
2.字符串数组和指针
当指针指向字符串常量时,通过指针不能修改字符串常量的值。如:
char* p = "hello world";
p[6] = ',';//错误,这是C语言标准未定义的操作
3.利用malloc分配内存
如果将某一个字符串复制到另一个某一个控件,需要用malloc开辟足够大的空间,不能忘记’\0’也要开辟空间。
4.空指针和空字符串的差别
空指针是指向0(NULL)的指针,C语言保证对空指针的操作是安全的
char *p = NULL; //C语言定义的NULL #define NULL 0,
空字符串是只有’\0’结束符的字符串,它在内存中是有存储空间的。
注意:NULL指针并不指向任何对象,在赋值和比较运算之外的其他运算符都是非法的。