内存单元的编号==地址==指针
int a = 10;
int *pa = &a;//取a的地址放在指针变量pa中(*pa是指针变量,int 是pa指向的是整型(int) 类型)
指针变量的大小取决于地址的大小:32位平台下地址是32个bit位(4个字节),64位平台下地址是64个bit位(8个字节)。只要是指针变量,大小就是4/8字节
指针的类型决定了对指针解引⽤的时候⼀次能操作⼏个字节。 例 char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。
void*指针 可以理解为⽆具体类型的指针,这种类型的指针可以⽤来接受任意类型地址。但 void* 类型的指针不能直接进⾏指针的+-整数和解引⽤的运算。
void test2()//测试const放在*的左边情况
{
int n = 10;
int m = 20;
const int* p = &n;
*p = 20;//error
p = &m; //ok
}
void test3()//测试const放在*的右边情况
{
int n = 10;
int m = 20;
int* const p = &n;
*p = 20; //ok
p = &m; //error
const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。
p-->p+i是跳过i个type类型的数据,跳了i*sizeof(type)个字节
int main()//指针运算---指针 +- 整数
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);//数组中元素地址是连续的
int* p = &arr[0];//p+1
for (i = 0; i < sz; i++)
{
printf("%d ", *p);
p++;//p=p+1
//printf("%d ",*(p+i));
}
return 0;
}
指针-指针的绝对值是指针之间的元素个数
int my_strlen(char *s)
{
char *p = s;
while(*p != '\0' )
p++;
return p-s;
}
int main()
{
printf("%d\n", my_strlen("abc"));
return 0;
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
造成野指针的原因:1,指针未初始化。2,指针越界访问。3,指针指向的空间释放。
一般为了防止野指针:assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。(assert(p != NULL);)
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a=%d b=%d\n", a, b);//a=10 b=20
Swap1(a, b);
printf("交换后:a=%d b=%d\n", a, b);//a=10 b=20
return 0;
}
在调⽤ Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,x和y确实接收到了a和b的值,但x,y的地址和a,b的地址不 ⼀样,相当于x和y是独⽴的空间,在Swap1函数内部交换x和y的值, ⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤ 的时候,是把变量本⾝直接传递给了函数,叫传值调⽤。
通过函数并没有实现交换,原因是实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。
void Swap2(int*px, int*py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:a=%d b=%d\n", a, b);//a=10 b=20
Swap2(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);//a=20 b=10
return 0;
}
这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这种函数调⽤⽅式叫传址调⽤。
传址调⽤可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;
若只是需要主调函数中的变量值来实现计算,可以采⽤传值调⽤。若函数内部要修改主调函数中的变量的值,就需要传址调⽤。
数组名是数组⾸元素(第⼀个元素)的地 址。但sizeof(数组名)表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节 。 &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。除此之外,数组名是数组⾸元素的地址。
int* p = arr; p[i] 是等价于 *(p+i)。因为数组名是首元素地址,可以赋值 给p,其实数组名arr和p在这⾥是等价的。
本质上数组传参传递的是数组⾸元素的地址。
void test(int arr[])//参数写成数组形式,本质上还是指针
{
printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{
printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
指针数组是存放指针的数组。指针数组的每个元素都是⽤来存放地址(指针)的。类型是int* /char*等。
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
return0;
}
上述代码是用指针模拟二维数组。
数组指针变量应该是存放的是数组的地址,能够指向数组的指针变量。例如int (*p)[10]是数组指针
存放个数组的地址,就得存放在数组指针变量中,例 int(*p)[10] = &arr;
⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏的地址。
void test(int (*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for(i=0; i
函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
函数指针类型如下:
函数指针举例:
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
//printf("%d\n", pf3(3, 5));
return 0;
}
回调函数是⼀个通过函数指针调⽤的函数。 如果把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。
例如qsort函数排序整数:
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}
qsort函数各个参数:
/void qsort(void* base, //指针,指向的是待排序的数组的第一个元素
// size_t num, //是base指向的待排序数组的元素个数
// size_t size, //base指向的待排序数组的元素的大小
// int (*compar)(const void*, const void*)//函数指针 - 指向的就是两个元素的比较函数
// );
在本代码中,就是在qsort函数中通过调用比较函数实现整数排序。
sizeof是操作符 ,计算操作数所占内存的⼤⼩,单位是字节 ,不关注内存中存放什么数据。统计时包含“\0”。
strlen是库函数,使⽤时需要包含头⽂件 string.h , srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数 ,关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界。