初阶:??
1.指针是什么
2.指针与指针类型
3.野指针
4.指针运算
5.指针与数组
6.二级指针
7.指针数组
进阶:
8.字符指针
9.数组指针
10.数组传参与指针传参
11.函数指针
12.函数指针数组
13.指向函数指针数组的指针
14.回调函数
15.指针和数组面试题解析
1.指针是什么:
指针是存放地址的变量,又叫指针变量。
一个指针变量的大小:32位机4个字节、64位机6个字节。
2.指针与指针类型:
指针的定义:int* p 、 char* pa 、 float* ptr 。
指针的赋值:int* p=&m;//将m的地址放在指针变量p中,p指向m的地址。
指针的解引用:n=*p;等价于n=m;这里的*p与定义时的int* p不一样、注意区分。
指针类型:整型指针类型(int* p)、字符型指针类型(char* p)、浮点型指针类型(float* p)。指针类型决定了指针在进行解引用的时候,能够访问空间的大小:int* p能访问4个字节、char* p能访问1个字节、浮点双精度型能访问8个字节。
? ? ? ? ??
? ? ? ? ? ?int*整形指针类型(赋值前)? ? ? ? ? ? ? ? ? ? ?int*整形指针类型(赋值后)
可以看出在解引用时,int* p能访问4个字节的内存空间。
3.指针运算:?
?指针可以进行加法与减法运算。根据指针类型的不同,指针加1有不同的效果。
对于char* 型,当前地址加1个字节(字符型变量占1个字节)。
对于int* 型,当前地址加4个字节(整型变量占4个字节)。
对于double* 型,当前地址加8个字节(双精度浮点型占8个字节)。
自增:有前置与后置之分:v=*p++? ?v=++*p
v=*p++==>v=*p;p++(先将*p的目标变量赋值给v,指针p再自增)
v=++*p==>++p;v=*p(先将指针p自增,再将自增后的变量*p赋值给v)
v=(*p)++==>v=*p;*p++(先将*p的目标变量赋值给v,目标变量*p再自增)
v=++(*p)==>++*p;v=*p(先将目标变量*p自增,再将自增后的变量*p赋值给v)
指针的相减运算:一定条件下,两个指向同种数据类型的指针之间可以相减,相减含义在于:两个地址的相减再除以数据类型(int、char、double)的字节数。
4.野指针:?
#include<stdio.h>
int main()
{
int arr[5]={1,2,3,4,5};
int* pa=arr;
int i=0;
for(i=0;i<6;i++)
{
*(pa++)=i;
}
return 0;
}
野指针的成因:1.未初始化的指针。2.指针越界访问。3.指针指向的空间释放。?
7. 指针数组:
指针数组与整型数组、字符数组一样,是数组。例如:整型数组存储的每一个元素都是整型;同理指针数组存储的每一个元素都是地址。
#include<stdio.h>
{
int a=3;
int b=5;
int arr1[]={1,2,3,4,5};
int arr2[]={2,3,4,5,6};
int arr3[]={3,4,5,6,7};
int* arr[]={arr1,arr2,arr3};
int* a[]={&a,&b};
for(int i=0;i<3;i++)
{
for(int j=0;j<5;j++)
{
printf("%d",*(arr[i]+j));
}
}
}
//打印结果:1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
9.数组指针:
数组指针就是个指针,就如整型指针一样:指向整型的指针-可以存放整型的地址,同理:数组指针就是指向数组的指针-可以存放数组的地址。
数组的地址表示方式:int arr[5]={0};//首元素地址表示法:arr或者&arr[0]。数组地址表示法:&arr。
例如:int arr[10]={1,2,3,4,5,6,7,8,9,10};现在需要把数组的地址存放起来,如下:
int? (*p)[10]=&arr;
&arr数组的地址肯定要用指针来存放,首先对比发现arr=*p,若没有括号则为:int* p[10],则为指针数组。注意定义时的*不是解引用操作,而是表明p为指针,[]表明为数组,结合起来就是数组指针。再例如:char* arr[5]={0};则他的数组指针为:
char* (*pa)[5]=&arr;
pa是指针变量的名字、‘*’说明pa是指针、[5]是指针指向的数组大小为5、char*说明指针指向的数组元素类型为字符。
数组指针的用法:
#include<stdio.h>? ? ? ? ? ? ? ? ? ?<====>? ? ? ??? ?#include<stdio.h>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int main()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?<====>? ? ? ? ? int main()? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <====>? ??? ?{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? int arr[10]={1,2,3,4,5,6,7,8,9,10};?<====>? ??int arr[10]={1,2,3,4,5,6,7,8,9,10};? ? ? ? ? ? ? ? ? ? ? ? ? int (*pa)[10]=&arr;? ? ? ? ? ? ? ? ? ? ?<====>? ? ? ?int* pa=arr;? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int i=0;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <====>? ? ? ? ? ? ? int i=0;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? for(i=0;i<10;i++)? ? ? ? ? ? ? ? ??<====>?? ? ? ? ? ?for(i=0;i<10;i++)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?<====>?? ? ? ? ? ?{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? printf("%d",(*pa)[i]);? ? ? ? ? ?? <====>? ? ? ? ? ? ? printf("%d",*pa+i));? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //(*pa)[i]=*(*pa+i))? ??? (*pa)[i] ==>pa是数组的地址,*pa是解引用,把数组地址解引用就是整个数组,加上[i]就是访问数组元素。*(*pa+i)==>*pa=arr首元素地址,首元素地址+i为指针向后移动i个元素,再加个*,就找到每个数组元素。上述代码太过繁琐,实际不这样用,为了加深理解。
一般而言数组指针要用在二维数组及以上才方便一些。
例如:
#include<stdio.h>
void print1(int (*pa)[5],int x,int y)//arr为一个一维数组的首地址传过来,用数组指针的形式来接收。
{
int i=0;
for(i=0;i<x,i++)
{
int j=0;
for(j=0;j<y;j++)
{
printf("%d ",*(*(pa+i)+j));//
}
printf("\n");
}
}
void print2(int arr[3][5],int x,int y)//数组形式的形参接受实参数组名arr。
{
int i=0;
int j=0;
for(i=0;i<x;i++)
{
for(j=0;j<y;j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5]={{1,2,3,4,5},{4,5,6,7,8},{5,6,7,8,9}};
print1(arr,3,5);//二维数组传参,传入数组名,行列大小
print2(arr,3,5);//二维数组的数组名其实是每行首元素的地址(这里其实是3个),一个3行5列的二维数组可以看成3个有5个元素的一维数组依次连接而成。故这里可以arr看成一个一维数组的首地址。
return 0;
}
二维数组:p[i][j]=*(p[i]+j)=*(*(p+i)+j)=(*(p+i))[j]
一维数组:p[i]=*(p+i)=*(arr+i)=arr[i]
加i、加j解引用就是[i]、[j]。
再来加深印象:
int arr[5]={2,3}:定义了一个大小为5的整型一维数组,数组存放的每个元素都是整型。
int *arr1[10]={&a,&b}:定义了一个大小为10的整型指针数组,数组存放的每个元素都是变量的地址,arr指向这个地址。
int (*arr2)[10]:定义了一个整型数组指针,指针arr指向的是一个大小为10的数组,数组的每个元素类型是整型。
int (*arr3[10])[5]: arr3与[]结合,那他是个数组。数组每个元素的类型是一个数组指针。该数组指针,指向的是一个大小为5的数组,此数组每个元素类型是整型。
10.数组传参与指针传参:函数设计时,难免要把【数组】和【指针】作为参数传递给函数
一维数组传参:
#include<stdio.h>
void test1 (int arr1[])
{}
void test1 (int arr1[5])
{}
void test1 (int* arr)
{}
void test2 (int* arr[5])
{}
void test2 (int* *arr)//二级指针存放的是一级指针变量的地址,不能存放一维数组的地址
{}
int main()
{
int arr1[5]={0};
int* arr2[5]={0};
test1(arr1);
test2(arr2);
}
采用数组形式传参时,数组大小可以省略。
二维数组传参:
#include<stdio.h>
void teat(int arr[][5],int x,int y)//二维数组形式,列不能省略
{}
void teat(int (*arr)[5],int x,int y)//数组指针形式
{}
int main()
{
int arr[3][5];
test(arr);
return 0;
}
二维数组的首元素地址是第一行的地址,第一行又是一个一维数组,所以二维数组首元素地址arr是一维数组的地址。
一级指针传参:
#include<stdio.h>
void print(int* p,int n)
{
int i=0;
for(i=0;i<n;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;
}
二级指针传参:
#include<stdio.h>
void test1(int* *p)
{
printf("num=%d ",**p);
}
int main()
{
int n=10;
int *p=&n;
int **pp=&p;
int* arr[10]={0};
test1(pp);//pp为二级指针变量,传过去也用二级指针来接收
test2(*p);//一级指针变量地址,传过去也有二级指针接受
test3(arr);
return 0;
}
11.函数指针:指向函数的指针,存放函数的地址。
#include<stdio.h>
int Add(int x,int y)
{
? ? int z=0;
? ? z=x+y;
? ? return z;
}
int main()
{
? int a=10;
? int b=20;
int arr[10]={0};
int (*p)[10]=&arr;
? int n=Add(a,b);
? printf("%s\n",&Add);
? printf("%s\n",Add);//两者打印的结果都为函数的地址。&函数名与函数名都是函数的地址
int (*ptr)(int,int)=Add;//类似指针数组的写法,(*ptr)表示指针,(int,int)表示函数,int,int表示
//函数参数类型,最前面的是int是函数值的返回类型。
printf("%d\n",(*ptr)(2,3));//指针调用Add函数,把参数2和3传进去,或者Add(2,3)。
return 0;
}
(*(void (*)())0)();(void (*)()为省略了指针变量的函数指针类型,(*(void (*)())0)把0强制类型转换,则0就是这个函数的地址,(*(void (*)())0)();(void (*)()再调用0地址处的该函数。
void (*signal(int,void (*)(int)))(int):signal(int,void (*)(int))是一个函数,他有一个整型参数,一个函数指针参数,他的返回类型也是一个函数指针为void(*? ? ?)(int)。
如果p是一个函数指针,那么在调用p时以下两种写法都是正确的:printf(“%d/n”,(p)(2,3))? or? ?printf("%d/n",(*p)(2,3))。
12.函数指针数组:把几个相同类型的函数的地址放到同一个数组里面。
#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;
}
int main()
{
int a=3;
int b=5;
int Add(a,b);
int Sub(a,b);
int Mul(a,b);
int Div(a,b);
int (*ptr[4])(int,int)={Add,Sub,Mul,Div};//函数指针数组的定义与初始化。
int i=0;
for(i=0;i<4;i++)
{
printf("%d\n",ptr[i](2,3));//函数指针的使用
}
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;
}
int main()
{
int n=0;
int a=0;
int b=0;
int (*ptr[5])(int,int)={0,Add,Sub,Mul,Div};
do{
printf("*********************************\n");
printf("*** 请输入对应数字 ********\n");
printf("*** 1-Add ********\n");
printf("*** 2-Sub ********\n");
printf("*** 3-Mul ********\n");
printf("*** 4-Div ********\n");
printf("*********************************\n");
scanf("%d",&n);
if(n>=1&&n<=5)
{
printf("请输入两个数\n");
scanf("%d%d",&a,&b);
printf("%d",ptr[i](a,b));
}
else if(n=0)
{
printf("请重新输入");
}
else
{
printf("输入错误");
}
}while(n);
return 0;
}
13.回调函数:通过函数指针将一个函数的地址作为参数传递给一个中间函数,在中间函数中实行这个函数的调用,这称中间函数为回调函数。
#include<stdio.h>
void print(char *str)
{
printf("hehe %s\n",str);
}
void test(void (*ptr)(char *))//被调函数参数用函数指针来接收,该函数指针又指向print函数
{
printf("test\n");
ptr("bit");//调用print函数,把bit传到print中
}
int main()
{
test(print);//函数地址作为参数传入到test函数中。
return 0;
}
打印结果:test
? ? ? ? ? ? ? ? ? hehe bite
则print称为回调函数。
冒泡排序(两两邻接的元素进行比较)普通版:缺陷是只能排序整型类型数组。
#include<stdio.h>
void bubble_sort(int arr[10],int sz)
{
for(int i=0;i<sz-1;i++)
{
for(int j=0;j<sz-1-i;j++)
{
if(arr[j]>arr[j+1])
{
int temp=0;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
int main()
{
int arr[10]={10,9,8,7,6,5,4,3,2,1};
int sz=sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz);
for(int i=0;i<sz;i++)
{
printf("%d",arr[i]);
}
return 0;
}
冒泡排序(两两邻接的元素进行比较)改进版:可以排序任意类型的数据(通过回调函数机制)
qsort库函数:实现排序功能,使用的算法思想为快速排序(选择排序有冒泡排序与快速排序)。
函数原型:void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));
base?Pointer to the first element of the array to be sorted.(数组起始地址)
num?Number of elements in the array pointed by base.(数组元素个数) size?Size in bytes of each element in the array.(每一个元素的大小字节) comparator?Function that compares two elements.(函数指针,指向比较函数,该函数自己实现。)
void* 类型的指针 可以接收任意类型变量的地址,但应注意,不能进行解引用和加减整除的操作。因为,void没有确定指针的类型,不知道是几个字节 void qsort (void* base, size num, size_t size,int (*compar)(const void*,const void*));
#include<stdio.h>
struct stu
{
char name[];
int age;
};
void Swap(char* buf1,char* buf2,int width)
{
for(int i=0;i<width;i++)
{
int temp=*buf1;
*buf1=*buf2;
*buf2=temp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base,int sz,int width,int (*cmp)(*e1,*e2))
{ //元素加字节,确定内存大小
for(int i=0;i<sz-1;i++)
{
for(int j=0;j<sz-1-i;j++)
{
if(cmp((char*)base+j*width,(char*)base+(j+1)*width)>0)
{//将void型的base强制转换为char*型,+j*width为+指针向后移动j*类型所占字节的空间
Swap((char*)base+j*width,(char*)base+(j+1)*width),width);
}
}
}
}
int cmp_int(const void* e1,const void* e2)//比较两个整型值大小的函数,*e1与*e2为接收将要比较的h两个数的地址,传进的参数应该是地址,该函数自己实现。
{
return *(int*)e1-*(int*)e2;//将传入e1与e2中的地址类型强制类型转换为int型,再解引用。
} //这个函数的返回值有3种,-1,0,1。
int cmp_stu_by_age(const void* e1,const void* e2)
{
return ((struct stu*)e1)->age-(struct stu*)e2->age)//->箭头优先级比圆括号的高。
}
void test4()
{
int arr[10]={9,8,7,6,5,4,3,2,1,0};
int sz=sizeof(arr)/sizeof(arr[0]);
bubble_sort(arr,sz,siezof(arr[0]),cmp_int);
}
void test5()
{
struct stu s[3]={{"zhangsan",18},{"lisi",22},{"wangwu",21}};
int sz=sizeof(s)/sizeof(s[0]);
bubble_sort(s,sz,sizeof(s[0]),cmp_stu_by_age);
}
int main()
{
test1();
test2();
test3();
test4();
test5();
return 0;
}
14.指向函数指针数组的指针(禁止套娃.jpg手动滑稽):
#include<stdio.h>
int Add(int x,int y)
{
return x+y;
}
int main()
{
int arr[10]={0};//定义了一个整型数组
int (*P)[10]=&arr;//数组的地址放在数组指针里面。
int (*pp)(int,int)=&Add;//函数的地址放在函数指针里面
int (*ppp[10])(int,int);//定义了一个函数指针数组,数组里面放的是多个相同类型函数的地址
int ((*pppp)[10])(int,int)=&ppp;//函数指针数组的地址放在指向函数指针数组的指针里面,pppp指针
//指向含有10个元素的数组,数组里面每个元素的类型都是函数指针类
//型。
return 0;
}
15.总结:
16.指针和数组笔试题解析:
#include<stdio.h>
int main()
{ //一维数组
int a[]={1,2,3,4};//4个整型元素*每个整型元素占4个字节
printf("%d\n",sizeof(a));//sizeof(数组名)-计算的是数组总大小-4*4=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",sizeof(&a[0]+1));//4
return 0;
}
?数据元素类型与地址的区别:
1、数据元素类型:整型占4个/8个字节。字符型占1个字节
2、地址字节大小不分数据类型是4/8个字节。
#include<stdio.h>
int main()
{ //字符数组
char arr[]={'a','b','c','d','e','f'};//6个字符元素*每个字符类型元素占1个字节
printf("%d\n",sizeof(arr));//sizeof(数组名)-计算的是数组总大小-6*1=6个字节。
printf("%d\n",sizeof(arr+0));//4个字节,arr-首元素地址大小不分数据类型是4/8。
printf("%d\n",sizeof(*arr));//是字符类型1个字节。
printf("%d\n",sizeof(arr+1));//是地址4个字节。
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]));//是地址4个字节。
printf("%d\n",sizeof(&arr[0]+1));/是地址4个字节。
return 0;
}
#include<stdio.h>
int main()
{ //字符数组
char arr[]={'a','b','c','d','e','f'};//6个字符元素*每个字符类型元素占1个字节
printf("%d\n",strlen(arr));//随机数,找不到'\0'
printf("%d\n",strlen(arr+0));//随机数
printf("%d\n",strlen(*arr));//(*arr)就是'a'=97,代码erro
printf("%d\n",strlen(arr+1));//1
printf("%d\n",strlen(arr[1]));//(*arr)就是'a'=98,代码erro
printf("%d\n",strlen(&arr));//随机数,不管你是首元素地址还是数组地址,都是从一个字符开始找不
//到'\0'
printf("%d\n",strlen(&arr+1));//随机数
printf("%d\n",strlen(&arr[0]));//随机数
printf("%d\n",strlen(&arr[0]+1));//随机数
return 0;
}
#include<stdio.h>
int main()
{ //字符串数组
char arr[]="abcdef";//6个字符元素*每个字符类型元素占1个字节
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));//4字节
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]));//4字节
printf("%d\n",sizeof(&arr[0]+1));//4字节
return 0;
}
int main()
{ //字符串数组
char arr[]="abcdef";//6个字符元素*每个字符类型元素占1个字节
printf("%d\n",strlen(arr));//6,从首元素地址开始向后计算直到遇到\0
printf("%d\n",strlen(arr+0));//6
printf("%d\n",strlen(*arr));//strlen函数参数是个指针,指向的是地址,此代码错误
printf("%d\n",strlen(arr+1));//5
printf("%d\n",strlen(arr[1]));//代码错误erro
printf("%d\n",strlen(&arr));//6
printf("%d\n",strlen(&arr+1));//随机值
printf("%d\n",strlen(&arr[0]));//6
printf("%d\n",strlen(&arr[0]+1));//5,从b的地址向后面数
return 0;
}
#include<stdio.h>
int main()
{ //字符串指针
char *p="abcdef";//此写法是把常量字符串的第一个字符地址放在了指针p里面了。
printf("%d\n",sizeof(p));//4个字节,计算指针变量(放的地址)的大小,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个字节
return 0;
}
int main()
{ //字符串指针
char *p="abcdef";//此写法是把常量字符串的第一个字符地址放在了指针p里面了。
printf("%d\n",strlen(p));//6
printf("%d\n",strlen(p+1));//5
printf("%d\n",strlen(*p));//erro
printf("%d\n",strlen(p[0]));//erro
printf("%d\n",strlen(&p));//随机值,指针p存放的是字符a的地址,&p:指针p的地址的长度肯定是随机值啊
printf("%d\n",strlen(&p+1));//随机值
printf("%d\n",strlen(&p[0]+1));//5,第一个元素的地址+1,
return 0;
}
#include<stdio.h>
int main()
{
//二维数组
int a[3][4]={0};
printf("%d\n",sizeof(a));//3*4*4=48个字节
printf("%d\n",sizeof(a[0][0]));//4个字节
printf("%d\n",sizeof(a[0]));//16个字节、a[0]表示第一行首元素地址==sizeof(数组名)=4*4。
printf("%d\n",sizeof(a[0]+1));//4个字节,第一行第二列元素的地址
printf("%d\n",sizeof(*(a[0]+1)));//4个字节
printf("%d\n",sizeof(a+1));//4个字节,a为首元素地址+1,跳过首行到第二行,求第二行地址的大小
printf("%d\n",sizeof(*(a+1)));//16个字节,第二行数组大小。
printf("%d\n",sizeof(&a[0]+1));//4个字节,第一行地址+1,跳到第二行,求第二行地址的大小
printf("%d\n",sizeof(*(&a[0]+1)));//16个字节,求第二行首元素的大小
printf("%d\n",sizeof(*a));//16个字节,a为第一行首元素地址,求第一行所有元素的大小
printf("%d\n",sizeof(a[3]));//16个字节
return 0;
}
数组名的意义:
1、sizeof(数组名)这里的数组名表示整个数组,计算的是整个数组的大小=元素个数*每个元素类型大小-单位是字节。
2、&(数组名)这里的数组名表示整个数组,取出的是整个数组的地址,地址就是4个字节,但是+1是跳过当前数组。
3、除以上情况,数组名都表示的是首元素地址,二维数组应该注意以一维数组去理解。
#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,1。
#include<stdio.h>
struct Test
{
int num;
char *pcName;
short sDate;
char cha[2];
short aBa[4];
}* p;
//假设p的地址为0x100000,已知结构体Test类型的变量大小是20个字节。
int main()
{
printf("%p\n",p+0x1);//跳过该结构体指向下一个地址:地址加20个字节,20转换为16机制数为:0x100014
printf("%p\n",(unsigned long)p+0x1);
printf("%p\n",(unsigned int*)p+0x1);
return 0;
}
//p为结构体指针,指向该结构体首元素地址
#include<stdio.h>
int main()
{
int a[4]={1,2,3,4};
int* ptr1=(int*)(&a+1);
int* ptr2=(int *)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}
//打印4,2 00 00 00
#include<stdio.h>
int main()
{
int a[3][2]={(0,1),(2,3),(4,5)};//圆括号里面是逗号表达式,逗号表达式的结果为里面的最后一个元素
int* p;
p=a[0];
printf("%d",p[0]);
}
//p[0]=*(p+0)=a[0][0]=1.
//指针p里面放的是第一行的地址,
//(0,1)=1,(2,3)=3,(4,5)=5
//a[3][2]={1,3,5};
#include<stdio.h>
int main()
{
int a[5][5];//定义了一个5行5列的二维数组
int (*p)[4];//定义了一个数组指针,指针p指向了4个元素的数组,数组里面元素类型是地址。
p=a;
printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);
return 0;
}
//FF FF FF FC,-4.
#include<stdio.h>
int main()
{
int aa[2][5]={1,2,3,4,5,6,7,8,9,10};
int* ptr1=(int*)(&aa+1);
int* ptr2=(int*)(*(aa+1));
printf("%d,%d",*(ptr1-1),*(ptr2-1));
return 0;
}
?
#include<stdio.h>
int main()
{
char* a[]={"word","at","alibaba"};
char* *pa=a;
pa++;
printf("%s\n",*pa);
return 0;
}
//打印at
#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);
printf("%s\n",*--*++cpp+3);
printf("%s\n",*cpp[-2]+3);
printf("%s\n",cpp[-1][-1]+1);
return 0;
}
?编程题:编写一个函数,可以实现字符串内容的逆序输出。
#include<stdio.h>
#include<string.h>
void reverse(char* str)
{
int len=strlen(str);//这里应该是求字符串长度,而不是大小。
char* left=arr;
char* right=str+len-1;
while(left<right)//这里考虑不用for循环的意图。
{
char temp=0;
temp=*left;
*left=*right;
*right=temp;
left++;
right--;
}
}
int main()
{
char arr[256]={0};
printf("请输入字符串:\n");
scanf("%s\n");//或者使用gets(arr)函数,意为读取一行。
reverse(arr);
printf("%s\n",arr[i]);//%s在打印时遇到空格就停止,比如:hello bit只会打印出hello。
return 0;
}
?编程题:计算sn=a+aa+aaa+aaaa+aaaaa的前n项和。如2+22+222+2222+22222.
#include<stdio.h>
int main()
{
int a=0;//具体数字a
int n=0;//要计算前几项和
scanf("%d%d",&a,&n);
int sum=0;//总值
int st=0;//当前项的值
for(int i=0,i<n;a++)
{
st=a*10+2;//通过发现而知:每一项都是前一项*10+a。
sum=sum+st;
}
return 0;
}
编程题:打印水仙花数,水仙花数‘153=1^3+5^3+3^3’,一个n位数等于各位上数的n次方之和。1、首先是判断一个数是几位数,例如:123,用i表示计数器,用123/10=12,i++。再用12/10=1,i++。最后用1/10=0,i++。退出循环,则123为3位数。
#include<stdio.h>//一个n位数等于各位上数的n次方之和。
#include<math.h>
int main()
{
int n=0;
for(n=0;n<=1000;n++)
{
int i=1;//计数器,确定n为几位数
int tmp=n;//定义临时变量,旨在执行下方程序时,不改变原本n的值。
int sum=0;
while(tmp/=10)//如果n不是只有1位的数(n/10)不等于0,则计数器i++。n=n/10。
{
i++;
}
while(tmp)
{
sum+=pow(tmp%10,n);
tmp/=10;
}
if(n==sum)
{
printf("n是水仙花数\n");
}
else
{
printf("n不是水仙花数\n");
}
}
return 0;
}
编程题:打印菱形。分上下部分打印,每部分需考虑的是行数与空格与星号的关系,空格和星号与行数的关系两个for循环表示,第一行1个星
#include<stdio.h>
int main()
{
13行13列
int x=0;
int y=0;
for(x=0;x<13;x++)
{
for
}
}
|