目录
指针
计算机中以字节为单位,每一个自己都有自己的地址编号。
指针就是地址
查看虚拟机位数:getconf LONG_BIT
32位操作系统
一级指针
指针变量:用来保存地址的一个变量
格式:存储类型 数据类型 *指针变量名;
int a=5;
int *p=&a; //定义了一个指针变量p,保存变量a的地址
printf("%p %p\n",&a,p); //打印结果一样,都是变量a的地址
printf("%d %d\n",a,*p);
//*****************************************
char b='y';
char *p=&b;
//注意:数据类型保持一致
int *p; //p是随机值,是野指针
int *p=NULL; //如果在定义时,指向单纯的定义,可以这么写
指针操作符
&: 取地址符:取变量的地址
*: 取内容:取地址里边的内容
*&a==a: *和&是互逆运算
&*a:错误(运算符优先级)
存储地址的指针本身就是一个变量,所以在内存中也需要空间存储
思考:想知道指针p的地址怎么办? &p
初始化
指针变量在使用前不仅要定义还要初始化,未初始化的指针变量不能随便使用,会产生野指针
1.把普通变量的地址赋值给指针变量
int a=5;
1)int *p=&a; //定义指针的同时赋值
2)int *p=NULL;
p=&a; //先定义后赋值
p是变量a的地址,而*p又可以取到地址里边的内容
通过*p改变指针指向的内容
*p = 8;
printf("%d %d\n",*p,a);
2.把数组的首地址赋值给指针变量
char a[]="abcd";
char *p=a; //指针指向数组的首地址,'a'
printf("%s %s\n",a,p);
printf("%c\n",*p);
3.把指针变量里边保存的地址赋值给另一个指针变量
float a=2.5;
float *p=&a;
float *q=p;//&a==p
*q=888.8;
printf("%.1f %.1f %.1f\n",a,*p,*q);
p++;//p指针变量,可以被赋值
注意:a++;//错误 a是地址常量,不能被赋值,不能为左值(=左边)
注意:定义的指针类型要与它所要指向的数据的类型一致;指针类型就是进行指针运算时其移动的位数
示例:
#include<stdio.h>
int main()
{
char a[6]="hello";
int *p=a;
printf("%c",*(p+1));
return 0;
}
//运行结果:o
指针运算
1)算术运算
char a[]="hello";
char *p=a;
printf("%c %p\n",*p,p);
p++;//p=p+1
printf("%c %p\n",*p,p);
int a[]={2,13,4,5};
int *p=a;
printf("%d %p\n",*p,p);
p++;
printf("%d %p\n",*p,p);
p++://指针向高地址方向移动一个数据单位(char 移动1字节,int移动4字节),指针的指向发生改变
p--://指针向低地址方向移动一个数据单位(char 移动1字节,int移动4字节),指针的指向发生改变
int a[]={2,13,4,5};
int *p=a;
i8nt *q=&a[2];
printf("%d %p\n",*q,q);
q--;
printf("%d %p\n",*q,q);
p+n:访问高地址方向第n个数据的地址,指针的指向不发生改变
p-n:访问低地址方向第n个数据的地址,指针的指向不发生改变
p++://指针向高地址方向移动一个数据单位(char 移动1字节,int移动4字节),指针的指向发生改变
p--://指针向低地址方向移动一个数据单位(char 移动1字节,int移动4字节),指针的指向发生改变
p++相当于p=p+1,p被重新赋值
p+2没有改变p的值,没有对p重新赋值
注意:两个地址之间的差值=两个地址之间相隔的元素个数
例:
int m = 100;
double n = 200;
int *p=NULL;
double *q=NULL;
p= &m;
q = &n;
printf(“m=%d &m=%p\n”, m, &m);
printf(“p=%p p+2=%p\n”, p, p+2);
printf(“n=%lf &n=%p\n”, n, &n);
printf(“q=%p q+2=%p\n”, q, q+2);
练习:
char s[32]="hello";
char *p=s;
p++;
char y=*--p;
printf("%c",y); //h
char a[ ]="interesting" , *p ;
p=a ;
while (*p!='s') { printf(“%c”,*p-32); p++ ; }
A)INTERESTING B)INTERESter C)INTEREster D)INTERE
//答案:D
2)关系运算符
> < == !=地址大的大于地址小的
指针之间进行关系运算,比较的是他指向的地址的高低
char a[]="hello";
char *p=a,*q=&a[2];
if(q>p)
printf("hahaha\n");
注意:指针指向同一个数组
练习:
int a[5]={10,20,30};
int *p=a;
printf("%d\n",++*p); //11
printf("%d\n",*p); //11
//a[5]={11,20,30};
p = a;
printf("%d\n",(*p)++); //11
printf("%d\n",*p); //12
//a[5]={12,20,30};
p=a+1;
printf("%d\n",*p++); //20
printf("%d\n",*p); //30
//a[5]={12,20,30};
p=a+1;
printf("%d\n",*++p); //30
printf("%d\n",*p); //30
思考:指针的大小是多少
易错题:
下面程序的输出结果是什么?
思路:
*(p++)+=100;//*p=*p+100;*p++;
printf("%d %d\n",*p,*(++p)); //printf打印多个参数,从右向左依次执行能够改变变量值的式子,会直接把改变后的变量带入。带入完毕后再从左往右依次输出
指针大小
int a=2;
int *p1=&a;
printf("%d\n",sizeof(p1)); //4
char b='q';
char *p2=&b;
printf("%d\n",sizeof(p2)); //4
short c=2;
short *p3=&c;
printf("%d\n",sizeof(p3)); //4
打印结构都是4,原因:
指针的大小取决于操作系统:
32位操作系统,指针大小为4
64位操作系统,指针大小为8
32位:单次运算能处理的最大数据位数
可以用8位16进制表示0x123...;1位16进制=4位2进制 32位=4字节
段错误
Segmentation fault (core dumped)
指针虽然灵活好用但是用不好会出现段错误。出现的原因有:访问不存在的内存地址、访问系统保护的内存地址 、修改只读的内存地址的内容、空指针废弃 (eg:malloc与free释放后,继续使用) 、堆栈溢出、内存越界 (数组越界)
访问不存在的内存地址
int *p=NULL;
*p=0;
访问系统保护的内存地址
int *p=(int *)8;
*p=99;
修改只读的内存地址的内容
char *p="world";
*p='h';
空指针废弃
int *p=NULL;
printf("%d\n",*p);
内存越界 (数组越界)
char a[3]={};
printf("%c\n",a[99999999]);
练习:
1.字符串char s[]="234",printf(%d\n",num);//234
2.编写一个程序实现功能:将字符串“Computer Science”赋值给一个字符数组,然后从第一个字母开始间隔的输出该字符串,用指针完成。结果:Cmue cec
3.字符串倒置(用指针实现) 如:hello->olleh
定义两个指针分别指向字符串的开头和结尾,前面的指针向后走,后面的指针向前走
易错题目:
1.
#include<stdio.h>
int main()
{
char s[32]="hello";
char *p=s;
p++;
char y=(*--p)++;
printf("%c",y);
return 0;
}
运行结果:h;
2.
3.
注意:
1.--*p; 是对p指向的内容
2.(*p)++; 最后是对p指向的内容++
3.*p++;是先算p++,再计算*。(单目运算符的运算顺序是自右向左);
总结
在输出或者赋值语句中:
*p++:先取内容,再移动指针的指向(向高地址方向移动一个数据单位)
(*p)++:先取内容*p ,再对内容+1
++*p:先取内容*p, 在对内容+1
++(*p):先取内容*p, 在对内容+1
练习:
1.给定一串字符"I love china",实现以单词为单位的逆序,如:"china love i"
解答:
思路:可以先全部倒过来:anihc evol i,然后再把每个单词倒过来---- 根据空格确定单词--空格既是一个单词的开头又是一个单词的结尾
示例:
char a[]="i love china",t;
char *p=a; //p:指向开头
char *q=p+strlen(a)-1; //q:指向结尾
char *k=NULL; //k:空格
while(p<q)
{
t=*p;
*p=*q;
*q=t;
p++;
q--;
} //全部倒置
puts(a);
p=q=a; //都指向开头
while(*p)
{
while(*p==' ') //针对于从第二个单词开始,找单词开头
p++;
q=p; //从当前的单次开始去找结尾
while(!q=' '&&*q!='\0')
q++;
k=q; //循环外部,此时q指向空格
q--;
while(p<q)
{
t=*p;
*p=*q;
*q=t;
p++;
指针修饰
const常量化 read only
(1)const修饰变量,变量的值不能通过变量名进行修改
const int a=5; // int const a=5;
a++; //错误
通过指针修改变量的值
int *p=&a;
*p=50; //可以
(2)修饰指针 *p 指针指向的内容不能修改,指针的指向可以修改
int a=5;
const int *p=&a; //修饰*p 指针指向的内容不能修改,指针的指向可以修改
*p=50; //错误
int b=50;
p=&b;
printf("%d\n",*p); //正确
(3)修饰p 指针的指向不能修改,指针指向的内容可以修改
int a=5;
int * const p=&a;
int b=50;
p=&b; //错误
************************************
*p=666; //正确
//指针的指向不能改,指针指向的内容也不能改
int a=5;
int const * const p=&a;
练习:
写出下面的声明:
(1) 指向常整形数的指针(整形数是不可修改的,但指针可以)
______________________________________________________________
(2)指向整型数的常指针(指针指向的整型数是可以修改的,但指针是不可修改的
void修饰指针
void a; //错误
void *p;//p是任意类型的指针 后期使用时,需要强转
强转指针:(int *)p 取内容:*((int *)p)
int a=5;
void *p=NULL;
p=&a;
printf("%p %d\n",(int *)p,*((int *)p)); //强转只是当时变了
//*********************************************************
int a=5;
void *p=NULL;
int *q=(int *)p;
q=&a;
大小端
大端:低地址存放高字节数据,高地址存放低字节数据
小端:低地址存放低字节数据,高地址存放高字节数据
举例:存储数据0x12345678,起始地址0x1000
0x1000 0x1001 0x1002 0x1003
大端:0x12 34 56 78
小端:0x78 56 34 12
int a=0x12345678;
char b;
b=(char)a;
printf("%x\n",b);//78
练习:
1.
(注意,第5行应该是a[6])
2.
3.已知字符数组a[10]和b[10]中元素的值递增有序,用指针实现将两个数组中元素按照递增顺序输出。
示例:
二级指针
一级指针:存放变量的地址
二级指针:存放一级指针的地址
格式:存储类型 数据类型 **指针变量名
int a=5;
int *p=&a;
int **q=&p;
q=&p
*q=*&p=p =&a *q=p=&a
**q=*p=*&a=a **q=*p=a
访问a的值:a *p **q
访问a的地址:&a p *q
访问p的地址:&p q
数组练习题
1.若有以下说明语句:int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};char c=’a’,d,g;则数值为4的表达式是()。
A)a[g-c] B)a[4] C)a[‘d’-‘c’] D)a[‘d’-c]
2.假设int型变量占两个字节的存储单元,若有定义:int x[10]={0,2,4};则数组x在内存中所占字节数为()。
A)40 B)6 C)12 D)20
3.下列合法的数组定义是()。
A)int a[]=”string”; B)int a[5]={0,1,2,3,4,5};
C)char a=”string”; D)char a[]={0,1,2,3,4,5};
4.若给出定义:char x[]=”abcdefg”;char y[]={‘a’,’b’,’c’,’d’,’e’,’f’,’g’};则正确的叙述为()。
A)数组x和数组y等价 B)数组x和数组y的长度相同
C)数组x的长度大于数组y的长度 D)数组y的长度大于数组x的长度
5.下列程序运行后的输出结果是()。
#include<stdio.h>
int main()
{
int n[3],t,j,k;
for(t=0;t<3;t++)
n[t]=0;
k=2;
for(t=0;t<k;t++)
for(j=0;j<3;j++)
n[j]=n[t]+1;
printf("%d\n",n[1]);
}
A)2 B)1 C)0 D)3
6.有以下程序,执行后输出结果是()。
#include<stdio.h>
int main()
{
int p[7]={11,13,14,15,16,17,18}, i=0,k=0;
while(i<7 && p[i]%2)
{
k =k+ p[i];
i++;
}
printf("%d\n", k);
}
A)58 B)56 C)45 D)24
7.有以下程序,执行后输出结果是()。
#include<stdio.h>
int main()
{
int m[][3]={1,4,7,2,5,8,3,6,9};
int i,j,k=2;
for(i=0; i<3; i++)
{
printf("%d ",m[k][i]);
}
}
A)4 5 6 B)2 5 8 C)3 6 9 D)7 8 9
8.以下能正确定义一维数组的选项是()。
A)int num[]; B) #define N 100
int num[N];
C)int num[0..100]; D) int N=100;
int num[N];
9、有以下程序,执行后输出结果是()。
#include<stdio.h>
int main( )
{
char a[]="abcdefg",b[10]="abcdefg";
printf("%d %d\n",sizeof(a),sizeof(b));
}
A)7 10 B)8 8 C)8 10 D)10 10
10、若有以下定义语句:char s[10];s=”abcd”;printf(“%s\n”,s);则运行结果是()。
A)输出abcd B)输出a C)输出ab cd D)编译不通过
11.不能把字符串“Hello!”赋给数组b的语句是()。
A)char b[10]={‘H’,’e’,’l’,’l’,’o’,’!’};
B)char b[10];b=”Hello!”;
C)char b[10]; strcpy(b,”Hello!”);
D)char b[10]=”Hello!”;
12、若有以下程序段,该程序段的输出结果是()。
char str[]=“a b \n \012 \\ \” ”;
printf(“%d”,strlen(str));
A)3 B)4 C)6 D)12
13.下列程序的输出结果是()。
#include <stdio.h>
main()
{
char ch[7]={"65ab21"};
int j,s=0;
for(j=0;ch[j]>='0'&&ch[j]<='9';j+=2)
s=10*s+ch[j]-'0';
printf("%d\n",s);
}
A)12ba56 B)66521 C)6 D)62
14.设已定义char a[10]和 int j,则下面输入函数调用中错误的是()。
A)scanf(“%s”,a); B)for(j=0;j<9;j++) scanf(“%c”,a[j]);
C)gets(a); D)for(j=0;j<9;j++) scanf(“%c”,&a[j]);
15、设已定义char x[8]和int j,为了给该数组赋值,下面语句中正确的是()。
A)x[8]=”Turbo C”; B)x=” Turbo C”;
C)x[]=”Turbo C”; D)for(j=0;j<7;j++) x[j]=getchar();
16.有如下程序,该程序的输出结果是()。
#include <stdio.h>
main()
{
int n[5]={0,0,0},j,k=2;
for(j=0;j<k;j++)
n[j]=n[j]+1;
printf("%d\n",n[k]);
}
A)不确定的值 B)2 C)1 D)0
17.写出下列成绩的输出结果:————————
#include <stdio.h>
void main()
{
int m[] = {1,2,3,4,5,6,7,8,9},i,j,k;
for(i=0;i<4;i++) //0 1 2 3
{
k=m[i];
m[i]=m[8-i];
m[8-i]=k;
for(j=o;j<9;j++)
printf("%d", m[j]);
putchar('\n');
}
}
18.有数组定义 int niNumberA[100]; 以下叙述不正确的是( )。(山大华天)
A. 数组 niNumberA 有100个元素
B. 数组 niNumberA 的最后一个元素是 niNumberA[100]
C. 数组 niNumberA 的第一个元素 *niNumberA
D. 数组 niNumberA 的字节数是 sizeof(int)*100
19.以下不能对二维数组 a 进行正确初始化的语句是 ( )。(山大华天)
A. int A[2][3] = {0};
B. int B[][3] = {{0, 1}, {0}};
C. int C[2][3] = {{0, 1}, {2, 3}, {4, 5}};
D. int D[][3] = {0, 1, 2, 3, 4, 5};
20. 下列对 C 语言字符数组的描述中错误的是 ( ) (鲁科安全)
A. 字符数组可以存放字符串
B. 字符数组中的字符串可以整体输入、输出
C. 不可以用关系运算符对字符数组中的字符串进行比较
D. 可以在赋值语句中通过赋值运算符 "=" 对字符数组整体赋值
答案:
- D 2.D 3.D 4.C 5.D 6.D 7.C 8.B 9.C 10.D 11.B 12.C 13.C 14.B 15.D 16.D 17.923456781
983456721
987456321
987654321
18.B 19.C 20.D