目录
指针:
1.指针是个变量,用来存放地址,地址唯一标识一块内存空间
2.指针的大小是固定的4/ 8个字节(32位平台/ 64位平台)
3.指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作时的权限
4. 指针的运算
字符指针
指针类型为:char*
#include <stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
char* p = "hello world";// 字符串是一个常量指针,表示首个元素的地址
return 0;
}
#include <stdio.h>
int main()
{
char str1[] = "hello world";
char str2[] = "hello world";
char* str3 = "hello world";
char* str4 = "hello world";
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");
printf("%p\n", str1);
printf("%p\n", str2);
printf("%p\n", str3);
printf("%p\n", str4);
return 0;
}
运行结果:
c和c++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。
用相同的常量字符串去初始化不同的数组时就会开辟初不同的内存块。
指针数组
指针数组:指针数组是一个存放指针的数组。
int* arr1[10]; // 是一个数组,每个元素的类型是int*
int** arr2[10]; // 是一个数组,每个元素的类型是int**
int*** arr3[10]; // 是一个数组,每个元素的类型是int***
char* arr4[10]; // 是一个数组,每个元素的类型是char*
char** arr5[10]; // 是一个数组,每个元素的类型是char**
数组指针
数组指针:是一种指针,指向数组的指针。
#include <stdio.h>
int main()
{
char arr[5];
char(*pa)[5] = &arr;// 是一个数组指针,数组的每个元素的类型为char
int* parr[5];
int* (*ppa)[5] = &parr;// 是一个数组指针,数组的每个元素的类型为int*
return 0;
}
&数组名、数组名
数组名表示首元素的地址,数组名加1跳过一个元素的地址
&数组名表示这个数组的地址,&数组名加1跳过整个数组的地址
#include <stdio.h>
int main()
{
int arr[10];
printf("arr = %p\n", arr);
printf("arr + 1 = %p\n\n", arr + 1);
printf("&(arr[0]) = %p\n", &(arr[0]));
printf("&(arr[0]) + 1 = %p\n\n", &(arr[0]) + 1);
printf("&(arr[0]) = %p\n", &(arr[0]));
printf("&(arr[1]) = %p\n\n", &(arr[1]));
printf("&arr = %p\n", &arr);
printf("&arr + 1 = %p\n", &arr + 1);
return 0;
}
数组指针的使用
#include <stdio.h>
int main()
{
int arr[10] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10 };
int(*p)[10] = &arr;
for (int i = 0; i < 10; i++) {
printf("%d ", *((*p) + i));
}
return 0;
}
#include <stdio.h>
void print(int(*p)[5], int row, int col)
{
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7} };
print(arr, 3, 5);
return 0;
}
arr是一个指针,指向包含5个元素的数组,所以arr是一个数组指针。
int arr[5]; arr 是一个数组,包含5个元素,每个元素的类型是int
int *parr1[10]; Parr1 是一个数组,包含10个元素,每个元素的类型是int*
int (*parr2)[10]; parr2 是一个指针,指向一个数组,这个数组的大小是10,每个元素的类型是int
int (*parr3[10])[5]; parr3是一个数组,有10个元素,每个元素的类型是数组指针,指向的数组有5个元素,每个元素的类型是Int。
数组传参,指针参数
写代码时,要把数组或者指针传给函数,那们函数的参数该如何设计?
一维数组传参
#include <stdio.h>
void test(int arr[]) // 正确
{
;
}
void test(int arr[10]) // 正确
{
;
}
void test(int* arr) // 正确
{
;
}
void test2(int *arr[20]) // 正确
{
;
}
void test2(int **arr) //正确,arr2是一个指针数组,数组的每个元素的类型是int*,可以用int**来接收
{
;
}
int main()
{
int arr[10] = { 0 };// 数组名相当于首元素的地址
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
return 0;
}
二维数组传参
#include <stdio.h>
void test(int arr[3][5])
{
; // 正确
}
void test(int arr[][])
{
; // 错误,形参是二维数组,行可以省略,列不可以省略。可以根据列来得到行的个数
}
void test(int arr[][5])
{
; // 正确
}
void test(int *arr)
{
; // arr 一个int类型指针,错误
}
void test(int *arr[5])
{
; // 错误,int *arr[5] 是一个指针数组
}
void test(int (*arr)[5])
{
; // 正确, int(*arr)[5] 是一个数组指针
}
void test(int **arr)
{
; // 错误, int **arr 是一个二级指针
}
int main()
{
int arr[3][5] = { 0 };
// 二维数组传进去是一个数组指针
test(arr);
return 0;
}
一级指针传参
#include <stdio.h>
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", *(p + i));
}
}
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);
return 0;
}
void test1(int *p)
{
;// p能接受,int类型的数组,int类型的指针
}
void test2(char* p)
{
;// p能接受char类型的一维数组,char类型的指针
}
二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
test(&p); // 还可以接受指针数组
return 0;
}
函数指针
函数指针,一个函数的地址
void (*pfun)();
(*pfun) 表明是一个指针,()表明是一个函数,void 表示返回类型为空
#include <stdio.h>
void test()
{
printf("1\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
// 函数的名称就是函数的地址
return 0;
}
(*(void (*)())0)();
// 0 是一个函数指针,它指向的函数的返回类型是void类型
void (*signal(int, void(*)(int)))(int);
// signal 是一个函数声明,他有两个参数,一个是int类型,一个是函数指针,该指针指向的函数的参数为int返回类型为void
// signal 的返回值也是一个函数指针,参数是int, 返回类型是void
// 简化:
typedef void(*pfun_t)(int); // pfunt_t 是一个函数指针
pfun_t signal(int, pfun_t); // pfunt_t 定义的变量,表示那个变量是函数指针,signal的返回值是一个函数指针
函数指针数组
函数指针数组:是一个数组,数组的每个元素的类型是函数指针。
int (*parr1[10])(); // 函数指针数组
转移表:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void menu()
{
printf("****0.exit*****************\n");
printf("****1.add ****2.sub ******\n");
printf("****3.mul ****4.div ******\n");
printf("***************************\n");
}
int main()
{
int x, y, input = 1, ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出程序\n");
break;
case 1:
printf("请输入操作数: ");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("请输入操作数: ");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("请输入操作数: ");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("请输入操作数: ");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
#include <stdio.h>
int add(int x, int y)
{
return x + y;
}
int sub(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x, int y)
{
return x / y;
}
void menu()
{
printf("*******************************\n");
printf("**** 0.exit *****\n");
printf("**** 1.add 2.sub *****\n");
printf("**** 3.mul 4.div *****\n");
printf("*******************************\n");
printf("*******************************\n");
}
int main()
{
int (*pf1)(int, int) = add;
int (*pf2)(int, int) = sub;
int (*pf3)(int, int) = mul;
int (*pf4)(int, int) = div;
int (*pfarr[5])(int, int) = { 0, add, sub, mul, div };
int x, y, ret, input;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
if (input == 0) {
printf("退出程序\n");
break;
} else if (input >= 1 && input <= 4){
printf("输入两个操作数:>");
scanf("%d %d", &x, &y);
ret = pfarr[input](x, y);
printf("ret = %d\n", ret);
} else {
printf("选择错误\n");
}
} while (input);
return 0;
}
指向函数指针数组的指针
指针指向一个数组,数组的元素全是函数指针。
#include <stdio.h> void test(const char* str) { printf("%s\n", str); } int main() { void (*pfun)(const char*) = test; // 函数指针 void (*pfunarr[5])(const char* str); // 函数指针数组 void (*(*ppfunarr)[10])(const char*) = &pfunarr; // 函数指针数组的指针 return 0; }
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作位参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方式直接调用的,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
qsort函数的使用:
#include <stdio.h>
#include <stdlib.h>
int cmp(const void* pa, const void* pb)
{
return *(int*)pa - *(int*)pb;
}
int main()
{
int arr[] = { 10, 9, 8, 7, 6, 5, 4 ,3, 2, 1, 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(int), cmp);
for (int i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int cmp(const void* pa, const void* pb)
{
return ((*(int*)pa) % 2) - ((*(int*)pb) % 2);
}
int main()
{
int arr[] = { 10, 9, 8, 7, 6, 5, 4 ,3, 2, 1, 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(int), cmp);
for (int i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
习题:
数组名是数组首元素的地址。
sizeof(数组名),计算的是整个数组的大小。
&数组名,数组的地址
int a[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(a)); // 16
printf("%d\n", sizeof(a + 0)); // 4
printf("%d\n", sizeof(*a)); // 4
printf("%d\n", sizeof(a + 1)); // 4
printf("%d\n", sizeof(a[1])); // 4
printf("%d\n", sizeof(&a)); // 4
printf("%d\n", sizeof(*&a)); // 16
printf("%d\n", sizeof(&a + 1)); // 4
printf("%d\n", sizeof(&a[0])); // 4
printf("%d\n\n", sizeof(&a[0] + 1)); // 4
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,数组的第二个元素的地址
// strlen 求字符串长度,需要\0,求的长度是未知的,strlen是库函数,sizeof是操作符
printf("%d\n", strlen(arr)); // 随机值
printf("%d\n", strlen(arr + 0)); // 随机值
printf("%d\n", strlen(*arr)); // 求字符a的长度,从地址'97'开始访问内存,strlen需要的是一个地址,从这个地址开始向后找\0,找到字符串的个数
printf("%d\n", strlen(arr[1])); // 内存访问冲突
printf("%d\n", strlen(&arr)); // 随机值
printf("%d\n", strlen(&arr + 1)); // 随机值
printf("%d\n\n", strlen(&arr[0] + 1)); // 随机值
char arr1[] = "abcdef";
printf("%d\n", sizeof(arr1)); // 7
printf("%d\n", sizeof(arr1 + 0)); // 4
printf("%d\n", sizeof(*arr1)); // 1
printf("%d\n", sizeof(arr1[1])); // 1
printf("%d\n", sizeof(&arr1)); // 4
printf("%d\n", sizeof(&arr1 + 1)); // 4
printf("%d\n\n", sizeof(&arr1[0] + 1)); // 4
printf("%d\n", strlen(arr1)); // 6
printf("%d\n", strlen(arr1 + 0)); // 6
printf("%d\n", strlen(*arr1)); // 非法访问内存
printf("%d\n", strlen(arr1[1])); // 非法访问内存
printf("%d\n", strlen(&arr1)); // 6
printf("%d\n", strlen(&arr1 + 1)); // 随机值
printf("%d\n", strlen(&arr1[0] + 1)); // 5
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 &p + 1 p 的地址加1,p后的地址
printf("%d\n\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\n", strlen(&p[0] + 1)); // 5
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); // 48
printf("%d\n", sizeof(a[0][0])); // 4
printf("%d\n", sizeof(a[0])); // 16
printf("%d\n", sizeof(a[0] + 1)); // 4,第一行第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1))); // 4
printf("%d\n", sizeof(a + 1)); // 4
printf("%d\n", sizeof(*(a + 1))); // 16
printf("%d\n", sizeof(&a[0] + 1)); // 4
printf("%d\n", sizeof(*(&a[0] + 1))); // 16
printf("%d\n", sizeof(*a)); // 16
printf("%d\n", sizeof(a[3])); // 16 int[4]
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));// 2 , 5
// *(a + 1) = a[1] = 2
// ptr == &a + 1 == &a[5]
// ptr - 1 = &a[4] *(ptr - 1) = 5
return 0;
}
#include <stdio.h>
struct Test
{
int Num; // 0 1 2 3
char* pcName; // 4 5 6 7
short sDate; // 8 9
char cha[2]; // 10 11
short sBa[4]; // 12 13 14 15 16 17 18 19
}*p;
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1); // 0x100014
printf("%p\n", (unsigned long)p + 0x1); // 0x100001
printf("%p\n", (unsigned int*)p + 0x1); // 0x100004
return 0;
}
%p 以地址的形式打印,前面的0不省略
%x 以十六进制的形式打印,前面的0省略
#include <stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1); // ptr1 = a[5]
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);// 4, 2000000
// ptr[-1] = *(ptr - 1) = *(a + 5 - 1) = a[4] = 4
// 小端:
// 01 00 00 00 02 00 00 00
// ptr2
// ptr2 : 00 00 00 02
// 以十六进制打印 02 00 00 00
return 0;
}
#include <stdio.h>
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
// (0, 1) 逗号表达式,(0, 1) == 1
int* p;
p = a[0];
printf("%d", p[0]);// 1
// p = a[0] = *(a + 0)
// p[0] = *(p + 0) = *( *(a + 0) + 0) = a[0][0]
return 0;
}
#include <stdio.h>
int main()
{
int a[5][5];
int(*p)[4];// 数组指针,指向的数组有4个元素,每个元素的类型是int,p + 1 跳16个字节
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);// fffffffc -4
// &a[4][2] = *( *(a + 4) + 2 ) == + 4 * 5 + 2 == 22
// &p[4][2] = *( *(p + 4) + 2 ) == + 4 * 4 + 2 == 18
// 18 -22 = -4
// 100000000 00000000 00000000 00000100 源码
// 111111111 11111111 11111111 11111011 反码
// 111111111 11111111 11111111 11111100 补码
// ff ff ff fc
return 0;
}
#include <stdio.h>
int main()
{
int aa[2][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10} };
int* ptr1 = (int*)(&aa + 1); // aa[1][6]
int* ptr2 = (int*)(*(aa + 1));//aa[1]
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); // 10, 5
// aa[1][6] - 1= aa[1][5] = 10
// aa[1] - 1 = aa[0][5] = 5
return 0;
}
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };// a 是一个数组,每个元素的类型是char*
char** pa = a; // pa 是数组的受字符串的地址,
pa++; // pa++ 来到了第二个字符串
printf("%s\n", *pa);//at
return 0;
}
#include <stdio.h>
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s\n", **++cpp); // POINT
//++cpp = &cp[1]
//*(cp[1]) = c + 2
//*(c + 2) = c[2] = POINT
printf("%s\n", *-- * ++cpp + 3); // ER
// ++cpp = &cp[2]
// *(&cp[2]) = c + 1 = c[1]
// --c[1] = c[0]
//*c[0] == ENTER
// +3 == ER
printf("%s\n", *cpp[-2] + 3); // ST
//cpp[-2] = &cp[0]
//*&cp[0] = c + 3 =
// *(c + 3) = c[3] = FIRST
// +3 == ST
printf("%s\n", cpp[-1][-1] + 1); // EW
// cpp[-1][-1] == *(*(cpp - 1) -1 )
// cpp - 1 = &cp[1] ==> *(cpp - 1) == cp[1] == c + 2
// cp[1] - 1 == c + 1 ==> *(c + 1) = NEW === + 1 === EW
return 0;
}