目录
1.字符指针
字符指针char*
#include <stdio.h>
int main()
{
char ch = 'w';
char* pc = //pc存的是字符变量的地址。
const char*p = "hello world";
//把常量字符串hello world的首字符地址存在了p中,
//并不是把所有字符都放在p中,字符串是存在常量区的,不能修改最好加上const
printf("%c\n",*p);//打印一个字符
printf("%s\n",p);//打印字符串
return 0;
}
例题分析:
#include <stdio.h>
int main()
{
//hello world 字符串常量都是存在常量区的。
//str1和str2数组是在栈区上的,是把常量区的hello world分别拷贝到str1和str2数组中进行初始化,
//这两个数组不是同一块空间,所以str1和str2的地址是不同的。
char str1[] = "hello world";
char str2[] ="hello world";
//hello world 是存在常量区的,str3和str4存的都是h的地址(首字符的地址),指向的都是常量区
//中的hello world 字符串常量。所以str3和str4是同一个地址。指向同一块空间。
const char *str3 = "hello world";
const 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");
return 0;
}
str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,它们实际会指向同一块内存。但是用相同的常量字符串去初始化不同数组的时候就会开辟出不同的内存块。
2.指针数组
指针数组是一个存放指针的数组。
int main()
{
int arr[10];//整型数组-存放整型的数组
char arr[5];//字符数组-存放字符的数组
int* arr[10];//整型指针数组-存放整型指针的数组
char* arr[5];//字符指针数组-存放字符指针的数组
return 0;
}
分析代码:
1.一般不这样使用的。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int* arr[4] = {&a,
//打印每个元素中的地址所对应的值
printf("%d\n",*(arr[0]));
printf("%d\n", *(arr[1]));
printf("%d\n", *(arr[2]));
printf("%d\n", *(arr[3]));
return 0;
}
2.一般这样使用
#include <stdio.h>
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[] = { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3;i++)
{
int j = 0;
for (j = 0; j < 5;j++)
{
printf("%d ",*(parr[i]+j));
// *(parr[i] + j)等价于*(*(parr + i) + j)等价于parr[i][j]
}
printf("\n");
}
return 0;
}
3.
#include <stdio.h>
int main()
{
const char* arr[5] = {"abcdef","bcdefg","hehe","haha","zhangsan"};
int i = 0;
for (i = 0; i < 5;i++)
{
printf("%s\n",arr[i]);
}
return 0;
}
3.数组指针
数组指针是一个指针。
#include <stdio.h>
int main()
{
int arr[10] = {0};
int*p1 = arr;//arr是首元素地址
//数组指针
int(*p2)[10] = //&arr是取数组的地址
//p2先和*结合,证明p2是一个指针变量,然后指向的是一个大小为10个元素的数组
//其中每个元素的类型是整型
//p1的类型是int*
//p2的类型是int(* )[10]
return 0;
}
&数组名和数组名
1.
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%p\n",arr);//arr表示数组首元素的地址,等价于&arr[0]
printf("%p\n",//数组首元素的地址
printf("%p\n", //&arr表示数组的地址。
printf("\n");
printf("%p\n",arr+1);//跳过一个整型(跳过4个字节)
printf("%p\n",1);//跳过一个整型(跳过4个字节)
printf("%p\n",1);//跳过一个数组(跳过40个字节)
return 0;
}
2.
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%d\n",sizeof(arr));//这也是一种特殊的情况,这里求的整个数组的大小(字节)
return 0;
}
总结:数组名是首元素的地址,但是有2个例外:
1.sizeof(数组名),这里的数组名是表示的整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2.&数组名,这里的数组名不是首元素的地址,数组名表示整个数组,所以取出的数组的地址。
练习:写出数组指针
#include <stdio.h>
int main()
{
int* arr[10]={0};
int*(*p)[10] =
//*和p结合告诉我们p是一个指针变量,[10]表示p指向一个数组,这个数组有10个元素,
//int* 数组的10个元素是int*类型的。p指向了一个指针数组。
int** p1 = arr;
//arr表示首元素的地址,因为arr[0]里放的是一个int*的整型指针变量,&arr[0]也就是取一级指针的地址,
//一级指针的地址需要使用二级指针来接受。
return 0;
}
数组指针的使用
1.一维数组的正常传参。
void print1(int arr[],int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
void print2(int* parr,int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(parr+i));
}
printf("\n");
}
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr) / sizeof(arr[0]);
//这两个是等价的,int arr[],也是传入的首元素地址
print1(arr,sz);
print2(arr, sz);
return 0;
}
一维数组不正常的传参,利用数组指针。一般不这样使用。
void print3(int(*p)[10],int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(*p + i) );//等价于p[0][i]等价于*(*(p+0)+i)等价于(*p)[i]
}
printf("\n");
}
#include <stdio.h>
#include <windows.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr) / sizeof(arr[0]);
print3(
return 0;
}
总之很别扭,一般不这样使用的。
一般数组指针是应用于二维数组。
void print1(int arr[3][5])//等价于void print1(int arr[][5])
{
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5;j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print2(int(*p)[5])
{
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ",*(*(p+i)+j));
}
printf("\n");
}
}
#include <stdio.h>
int main()
{
int arr[3][5] = { {1,2,3,4,5},
{2,3,4,5,6},
{3,4,5,6,7}
};
print1(arr);
printf("\n");
print2(arr);
return 0;
}
对数组指针的个人理解:
在一维数组arr[10]中:数组指针p=&arr,p+1是跳过整个数组,*p相当于找到了arr也就是首元素的地址,*((*p)+0)就相当于arr[0],*((*p)+1)相当于arr[1]
在二维数组中arr[3][5]中:数组指针p=arr,arr相当于首元素地址相当于&arr[0],因为二维数组的首元素就是一个一维数组,因此二维数组的数组名就相当于是一个一维数组的地址了。所以p+1是跳过这个一维数组, * (p+0)相当于找到了arr[0]也就相当于找到了一维数组的数组名(首元素地址),这个一维数组有5个元素, *(*(p+0)+1)相当于arr[0][1]。
分析下面代码:
#include <stdio.h>
int main()
{
int arr[5];//arr是一个整型数组
int *parr1[10];//parr1是一个指针数组,该数组有10个元素,每个元素是int* 的类型
int(*parr2)[10];//parr2是一个指针,指向一个数组的指针,该数组有10个元素,每个元素是int
int(*parr3[10])[5];//[]优先级大于* parr3是一个数组,该数组有10个元素,每个元素是int(*)[5]的类型,
//每个元素分别指向一个数组,该数组有5个元素,每个元素是int
}
int(*parr3[10])[5]:
4.数组传参和指针传参
4.1一维数组传参
#include <stdio.h>
void test(int arr[])//数组传参可以省略数组大小
{}
void test(int arr[10])//这个10没有意义可以写100等等 不建议。
{}
void test(int *arr)
{}
void test2(int *arr[20])//int* arr[]也可以
{}
void test2(int **arr)//一级指针的地址需要用二级指针来接受,因为arr2表示首元素地址。
//arr2数组中的所有元素是int* 类型的,所以是一级指针的地址
{}
int main()
{
int arr[10] = { 0 };
int *arr2[20] = { 0 };
test(arr);//arr表示数组元素
test2(arr2);//arr2表示首元素地址
return 0;
}
4.2二维数组传参
void test(int arr[3][5])
{}
void test(int arr[][5])
{}
void test(int(*arr)[5])
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
int main()
{
int arr[3][5] = { 0 };
test(arr);
}
4.3一级指针传参
#include <stdio.h>
void print(int *p, int sz)
{
int i = 0;
for (i = 0; i<sz; i++)
{
printf("%d\n", *(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]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
当一个函数的参数部分为一级指针的时候,函数能接受什么参数?
void test1(int* p)
{}
int main()
{
int a = 10;
test1(
int arr[10] = { 0 };
test1(arr);
int *p = NULL;
test1(p);
return 0;
}
4.4二级指针传参
#include <stdio.h>
void test(int** ptr)
{
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p =
int **pp =
test(pp);
test(//1级指针的地址,需要使用二级指针来接收
return 0; }
当函数的参数为二级指针的时候,可以接收什么参数?
#include <stdio.h>
void test(int** ptr)
{}
int main()
{
int *p = NULL;
test(
int **pp =
test(pp);
int* arr[10] = { NULL };
test(arr);
return 0;
}
5.函数指针
整型指针:指向整型的指针
数组指针:指向数组的指针
函数指针:指向函数的指针
首先看一段代码:
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
printf("%p\n",Add);
printf("%p\n",
return 0;
}
结果发现函数名和&函数名是一样的。 都是函数地址。
那么函数地址是怎么保存呢?
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
//两种赋值都是可以的
int(*pf)(int, int) = Add;
//pf是一个指针变量,指向Add函数的指针,函数参数类型是两个int类型, 返回值类型为int
//对比数组指针
int(*pf)(int, int) =
return 0;
}
使用函数指针调用函数:
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int, int) = Add;
//函数指针调用函数
int ret = (*pf)(4,5);
printf("%d\n",ret);
//不解引用也是可以的。
int ret=pf(10,5);//其实解引用写不写,写多少都没关系,只是摆设
printf("%d\n",ret);
return 0;
}
分析下面代码:
《C陷阱和缺陷》
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
//代码1
(* ( void (*)() )0)();
// 把0强制类型转换为void(*)()的一个函数指针,并解引用0地址处的函数(调用0地址处的函数),该函数返回值为void,参数没有
//代码2
void(* signal(int, void(*)(int)) )(int);
//这个代码是一次函数声明
//signal是一个函数有2个参数,参数一个是整型,一个是函数指针 该函数指针指向的函数的函数参数是一个int类型
//返回值是void类型,signal函数的返回值是 一个函数指针,该函数参数为int,返回值为void
void(*)(int)signal(int, void(*)(int));//这样写是不对的。
对函数指针进行typedef简化:
void(* signal(int, void(*)(int)) )(int);
//简化
typedef void(*pfun_t)(int); //对void(* )(int)函数指针类型起了别名pfun_t
pfun_t signal(int, void(*)(int));
6.函数指针数组
数组是一个存放相同类型数据的存储空间,指针数组:
int *arr[10];
//数组的每个元素是int*
那把一个函数的地址存到一个数组中,那这个数组就叫函数指针数组,函数指针数组定义:
int (*parr1[10])(); // parr1先和[]结合,说明parr1是数组,数组的内容是int(*)()类型的函数指针。
//存放函数指针的数组
函数指针数组的用途:转移表
计算器:
1.不使用函数指针数组
#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("******************************\n");
printf("****1.add 2.sub*********\n");
printf("****3.mul 4.div*********\n");
printf("****0.exit ***********\n");
}
int main()
{
int x, y;
int input = 0;
int ret = 0;
do{
menu();
printf("请选择\n");
scanf("%d",
switch (input)
{
case 1:
printf("请输入操作数\n");
scanf("%d %d",
ret=add(x, y);
printf("%d\n",ret);
break;
case 2:
printf("请输入操作数\n");
scanf("%d %d",
ret=sub(x, y);
printf("%d\n", ret);
break;
case 3:
printf("请输入操作数\n");
scanf("%d %d",
ret=mul(x, y);
printf("%d\n", ret);
break;
case 4:
printf("请输入操作数\n");
scanf("%d %d",
ret=div(x, y);
printf("%d\n", ret);
break;
case 0:
printf("退出\n");
break;
default:
printf("输入有误,请重新选择\n");
break;
}
} while (input);
return 0;
}
2.使用函数指针数组
#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("******************************\n");
printf("****1.add 2.sub*********\n");
printf("****3.mul 4.div*********\n");
printf("****0.exit ***********\n");
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*parr[5])(int, int) = {NULL,add,sub,mul,div};//转移表 《C和指针》
while (input)
{
menu();
printf("请选择\n");
scanf("%d",
if (input >= 1 && input <= 4)
{
printf("请输入两个操作数\n");
scanf("%d %d",
ret=parr[input](x, y);
printf("%d\n",ret);
}
else if (input==0)
{
printf("退出\n");
}
else
{
printf("输入错误,重新输入\n");
}
}
return 0;
}
3.使用函数指针:我是用函数指针调用该函数,而不是直接调用函数本身,这就叫回调函数。必须使用函数指针完成。
#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("******************************\n");
printf("****1.add 2.sub*********\n");
printf("****3.mul 4.div*********\n");
printf("****0.exit ***********\n");
}
void calc(int(*p)(int,int))
{
int x, y;
int ret = 0;
printf("请输入操作数\n");
scanf("%d %d",
ret = p(x,y); //这里使用函数指针去调用函数,回调函数。
printf("%d\n", ret);
}
int main()
{
int input = 0;
do{
menu();
printf("请选择\n");
scanf("%d",
switch (input)
{
case 1:
calc(add);
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
case 0:
printf("退出\n");
break;
default:
printf("输入有误,请重新选择\n");
break;
}
} while (input);
return 0;
}
7.指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的每个元素是函数指针。
int(*(*parr)[10])(int,int)//函数指针数组的指针
*和parr结合,说明parr是一个指针,[10]表示该指针指向一个数组,该数组有10个元素,int(* )(int,int)表示每个元素是函数指针。
整合一下:
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*) =
return 0;
}
8.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的时间或条件发生时由另外的一方调用的,用于对该时间或条件进行响应。
代码分析:
1.标准的冒泡排序,只能固定好类型,不能做到通用。
void bubble(int* arr,int sz)
{
int i = 0;
for (i = 0; i < sz - 1;i++)
{
int j = 0;
for (j = 0; j < sz-1-i;j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
int arr[10] = { 8, 9, 7, 6, 5, 4, 3, 2, 1, 0 };
bubble(arr,sizeof(arr)/sizeof(arr[0]));
int i = 0;
for (i = 0; i < sizeof(arr) / sizeof(arr[0]);i++)
{
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}
2.观察库函数里的qsort:
首先先知道void* 指针变量的作用:
int main()
{
double d = 3.14;
int a = 3;
int*p1 = //把double* 类型给int * 类型不兼容的。会报警告
return 0;
}
int main()
{
double d = 3.14;
int a = 3;
void* p1 = //void* 指针变量可以接收任意类型的指针
void* p2 =
return 0;
}
发现不会报错,所以void* 类型的指针变量可以接收任意类型的指针(整型指针,字符指针,结构体指针等)。但是是不能进行运算的,不能进行+-整数,不能解引用。
知道void* 指针变量的作用后,分析qsort函数,函数原型:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
void* base:要排序的数组
size_t num:元素个数
size_t width:一个元素大小(单位字节)
int (__cdecl *compare )(const void *elem1, const void *elem2 ):函数指针,需要传函数地址
<stdlib.h> 头文件中
比较函数要求:
qsort函数的使用:
1.对整型排序
#include<stdio.h>
#include<stdlib.h>
void print(int* arr,int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
//比较两个整型的函数
int int_cmp(const void* p1,const void* p2)
{
return *((int*)p1) - *((int*)p2);//降序只需要改一下p2-p1
}
int main()
{
int arr[10] = {8,9,2,3,1,4,5,6,7,0};
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]),int_cmp);
print(arr, sizeof(arr) / sizeof(arr[0]));
return 0;
}
结果:
2.对结构体类型排序(按照名字)
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
struct student
{
char name[10];
int age;
};
int struct_cmp_name(const void* p1,const void* p2)
{
//字符串比较strcmp
return strcmp(((struct student*)p1)->name ,((struct student*)p2)->name);
}
int main()
{
struct student st[3] = { { "zhangsan", 18 }, { "lisi", 19 }, {"wangwu",20} };
qsort(st, sizeof(st) / sizeof(st[0]), sizeof(struct student), struct_cmp_name);
return 0;
}
没有排序前:
排序后:
知道怎么用了,我们来使用回调函数来模拟实现qsort(采用冒泡方式)
以整型比较:
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
void int_cmp(const void* e1, const void* e2)
{
return *((int*)e1) - *((int*)e2);
}
void Swap(void* p1,void* p2,int size)//size一个元素的大小
{
int i = 0;
for (i = 0; i < size;i++)
{
char temp = *(((char*)p1) + i);
*(((char*)p1) + i) = *(((char*)p2) + i);
*(((char*)p2) + i) = temp;
}
}
//仿照qsort函数来写函数参数
void My_qsort(void* base,int count,int size,int(* cmp)(const void* e1,const void* e2))
{
int i = 0;
for (i = 0; i < count - 1; i++)
{
int j = 0;
for (j = 0; j < count - 1 - i;j++)
{
if (cmp(((char*)base) + j*size, ((char*)base) + (j+1)*size) >0) //把元素的首地址传过去
{
Swap(((char*)base) + j*size, ((char*)base) + (j + 1)*size,size);
}
}
}
}
int main()
{
int arr[10] = {9,8,5,6,7,4,2,3,1,0};
My_qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),int_cmp);
int i = 0;
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
9.指针的练习
1.
#include <stdio.h>
int main()
{
int a[] = { 1, 2, 3, 4 };
printf("%d\n", sizeof(a));//16整个数组大小
//数组名单独放在sizeof中,整个数组的大小。
printf("%d\n", sizeof(a + 0));//4/8 首元素地址
printf("%d\n", sizeof(*a));//4 首元素大小 *a=a[0]
printf("%d\n", sizeof(a + 1));//4/8 第二个元素地址
printf("%d\n", sizeof(a[1]));//4 第二个元素大小
printf("%d\n", sizeof(//4/8 整个数组的地址
printf("%d\n", sizeof(*// 16 等价于sizeof(a);对数组地址解引用拿到的就是整个数组。
printf("%d\n", sizeof( 1));// 4/8 跳过整个数组的地址
printf("%d\n", sizeof(// 4/8 首元素地址
printf("%d\n", sizeof( 1)); //4/8 第二个元素地址
return 0;
}
2.
#include <stdio.h>
int main()
{
//字符数组
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));// 1
printf("%d\n", sizeof(arr[1]));// 1
printf("%d\n", sizeof(// 4/8
printf("%d\n", sizeof( 1));// 4/8
printf("%d\n", sizeof(&arr[0] + 1));// 4/8
printf("%d\n", strlen(arr));//随机值
printf("%d\n", strlen(arr + 0));//随机值
printf("%d\n", strlen(*arr));//报错 把'a'ASCLL值当成一个地址,非法访问指针,报错
printf("%d\n", strlen(arr[1]));//报错 把'a'当成一个地址
printf("%d\n", strlen(// 随机值 &arr也是从起始地址开始的 这里的传参类型不同会警告
printf("%d\n", strlen( 1));// 随机值,这里的传参类型不同会警告
printf("%d\n", strlen(&arr[0] + 1));// 随机值
}
去除2个报错的结果为:
3.
#include <stdio.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//整个数组大小,7 ,后面有一个\0
printf("%d\n", sizeof(arr + 0));//首元素地址,4/8
printf("%d\n", sizeof(*arr));// 1
printf("%d\n", sizeof(arr[1]));// 1
printf("%d\n", sizeof(// 4/8
printf("%d\n", sizeof( 1));// 4/8
printf("%d\n", sizeof(&arr[0] + 1));// 4/8
printf("%d\n", strlen(arr));// 6
printf("%d\n", strlen(arr + 0));// 6
//printf("%d\n", strlen(*arr));// 把字符'a'当成地址,报错
//printf("%d\n", strlen(arr[1]));// 把字符'a'当成地址,报错
printf("%d\n", strlen(// 6 &arr也是从首元素地址开始的。
printf("%d\n", strlen( 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));// 5
return 0;
}
去除2个报错的,结果为:
4.
#include <stdio.h>
#include<string.h>
int main()
{
const char *p = "abcdef";
printf("%d\n", sizeof(p)); //4/8 ,p是指针变量
printf("%d\n", sizeof(p + 1));//4/8
printf("%d\n", sizeof(*p));// 1
printf("%d\n", sizeof(p[0]));// 1
printf("%d\n", sizeof(// 4/8 取指针变量的地址
printf("%d\n", sizeof( 1));// 4/8 跳过一个指针变量char*
printf("%d\n", sizeof( 1));// 4/8
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p + 1));//5
//printf("%d\n", strlen(*p));//把字符a当成地址,会报错。
//printf("%d\n", strlen(p[0]));//把字符a当成地址,会报错
printf("%d\n", strlen(//把指针变量的地址当作参数,随机值
printf("%d\n", strlen( 1));//把指针变量的地址+1当作参数,跳过一个char*,随机值
printf("%d\n", strlen( 1));//5
return 0;
}
去掉两个报错的,结果为:
5.
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)( 1);
printf("%d,%d", *(a + 1), *(ptr - 1));//*(a + 1)等价于a[1]
//结果为2 ,5
return 0;
}
6.
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//计算的到结构体是20个字节
int main()
{
printf("%p\n", p + 0x1); //跳过20个字节,0x100014
printf("%p\n", (unsigned long)p + 0x1); // 一个无符号长整形+1,%P是打印内存里的值,即补码以地址的形式打印0x100001
printf("%p\n", (unsigned int*)p + 0x1);//跳过一个整型,0x100004
return 0;
}
7.
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)( 1);
int *ptr2 = (int *)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
//4 和 0x02 00 00 00
return 0;
}
8.
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式,结果为最后的结果
int *p;
p = a[0];
printf( "%d", p[0]); //1
return 0;
}
9.
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - //%p打印的是内存里的值,即补码:-4的补码:11111111 11111111 11111111 11111100 ,0xff ff ff fc
%d打印为:-4
return 0;
}
10.
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)( 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10 ,5
return 0;
}
11.
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
结果为at,pa++跳过一个char*
12.
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
printf("%s\n", *--*++cpp+3);//ER
printf("%s\n", *cpp[-2]+3);//ST
printf("%s\n", cpp[-1][-1]+1);//EW
return 0;
}
++的优先级比解引用高。
+比解引用低