一、数组意义与数组定义?
1、需要同时定义多个相同类型的变量,则考虑使用数组。
2、数组定义公式:元素数据类型数组名字[元素个数]
二、数组初始化
1)定义同时初始化
-> 元素数据类型 数组名字[元素个数] = {初始化列表,每一个成员之间使用","分开}
int A[3] = {100,200,300}; //编译通过
int A[3] = {100,200}; //编译通过
第三个没有赋值的成员等于0
int A[3] = {100,200,300,400}; //编译警告
警告: warning: excess elements in array initializer
int A[] = {100,200}; //编译通过 决定了下标等于2
int A[] = {100,200,300}; //编译通过 决定了下标等于3
int A[] = {100,200,300,400}; //编译通过 决定了下标等于4
2)先定义,没有初始化
int a; -> 局部变量 -> 随机值
int a; -> 全局变量 -> 0
int A[3]; -> 局部变量 -> 全部元素都是随机值
int A[3]; -> 全局变量 -> 全部元素都是0
int A[3] = {100,200}; -> 局部变量 -> 最后一个元素是0
int A[3] = {100,200}; -> 全局变量 -> 最后一个元素是0
3)先定义,后初始化 -> 一般结合循环来完成!
int A[3];
A = {100,200,300}; //编译出错
int A[3];
A[3] = {100,200,300}; //编译出错
小结论:定义数组时没有整体初始化,则之后都不能整体初始化,只能单个初始化。
int A[3];
A[0] = 100;
A[1] = 200;
A[2] = 300;
三、数组的下标
int A[3]; -> 在内存空间中连续申请12个字节,使用变量A间接访问这片内存空间,在访问数组中,使用下标来对成员进行访问。
例如:
int A[N] -> 下标范围: 0~N-1
记住: 最后一个元素是A[N-1],而不是A[N]
四、研究数组的名字含义
1、当数组名作用于sizeof()时,数组名代表这个数组的内存空间。
sizeof() -> 计算内存空间的大小
例子:
int main()
{
int A[3];
printf("%d\n",sizeof(A)); //12
}
2、当数组名不作用于sizeof()时,数组名代表数组首元素的地址。
int A[3];
数组名: A
首元素: A[0]
首元素的地址: &A[0]
结论: A = &A[0]
例子:
int main()
{
int A[3] = {100,200,300};
printf("A[0] = %d\n",A[0]);
printf("&A[0] = %p\n",&A[0]); //0xbfec8cb4
printf("A = %p\n",A); //0xbfec8cb4
return 0;
}
sizeof(粤嵌) -> 计算整个粤嵌的大小
粤嵌 -> 仅仅代表整个粤嵌第一层楼的地址
五、指针的概念
1、什么是指针?什么是指针变量?指针干什么用?
指针 指向是内存上地址,例如: 0xbfec8cb4
指针变量指向是专门用于存放地址的变量 p
指针是唯一的地址,所以确定申请的内存空间在哪里。
例子:
int a; -> 在内存申请一片内存空间,使用变量a间接访问这片内存
&a; -> 获取a变量的地址 &-> 取址符
2、究竟&a获取到地址,存放在哪里?
指针是一个地址,地址就应该存在指针变量中。
3、指针变量如何定义?
指针变量怎样定义取决于指向的内容的数据类型。
例如: int -> int *p
char -> char *p
double -> double *p
定义步骤:
1)先写一个 *
2)在*后面写一个指针变量名 *p
3)确定指向的内容 int a;
4)把第3步的内容的变量名去掉 int
5)把第4步的结果写在第2步结果前面 int *p
结果:int *p -> 指针变量,该变量指向一个整型数据!
变量定义公式: 数据类型 + 变量名
变量名:p
数据类型: int*
例子:
int a=100; -> 在内存申请一片内存空间,使用变量a间接访问这片内存
&a; -> 获取a变量的地址 &-> 取址符
int *p = &a; -> 将a变量的地址赋值给指针变量p
4、已知指针变量的值,如何求出该地址指向的内容是什么?
取地址: 已知变量值a,求地址值。 &a
解引用: 已知地址值p,求变量值。 *p
例子:
int a = 100; -> 在内存申请一片内存空间,使用变量a间接访问这片内存
&a; -> 获取a变量的地址 &-> 取址符
int *p = &a; -> 将a变量的地址赋值给指针变量p
int b = *p; -> 解引用出p地址指向的值,再赋值给变量b
========================例子============================
#include <stdio.h>
int main()
{
int a = 100;
int *p = &a;
printf("a = %d\n",a); //100
printf("&a = %p\n",&a); // 0xbfde90c8
printf("p = %p\n",p); // 0xbfde90c8
printf("*p = %d\n",*p); //100
return 0;
}
=======================================================
练习1: char b=‘A’ 能不能赋值给int *p ? -> 不可以,指针类型不对,只能是char *
char b = 'A';
char *p = &b;
printf("*p = %c\n",*p); // 'A'
printf("*p = %d\n",*p); // 65
5、指针的内存空间多大?
指针是一个地址,在linux系统32位中,地址长度都是4字节,所有指针变量都是4字节。
sizeof(指针变量名) = 4
#include <stdio.h>
int main()
{
int a = 100;
char b = 'A';
int *pa = &a;
char *pb = &b;
printf("%d\n",sizeof(a));//4
printf("%d\n",sizeof(b));//1
printf("%d\n",sizeof(pa));//4
printf("%d\n",sizeof(pb));//4
return 0;
}
六、野指针与空指针
1、什么是野指针?
定义了一个指针变量之后,但是没有进行初始化,指针变量就会赋值一个随机值,指向一个未知区域。
int a; -> 随机值
a = 5;
int *p; -> 随机值 -> 这时候p就称之为野指针。
p = &a;
2、如何解决野指针?
1)在定义指针初始化指针指向的区域。
例子:
int a;
int *p = &a;
2)使用空指针 -> NULL
NULL其实是一个宏定义,真正NULL是等于0,被定义在一个头文件:
#if defined(__cplusplus)
#define NULL 0
#else
#define NULL ((void *)0)
#endif
例子:
int *p = NULL;
3)其实空指针只是安全区域中其中一个地址,安全区域地址范围: 0x00000000~0x08048000,只要指针指向该范围,那么指针就不会指向别的区域了。
int a;
int *p = (0x00000000~0x08048000);
p = &a;
七、如果访问了安全区域的数据,会出现什么情况?
例子:
#include <stdio.h>
int main(int argc,char *argv[])
{
int *p = (int *)0x08047000;
int a = *p;
printf("p = %p\n",p);
return 0;
}
编译: 通过
执行: 出现段错误 Segmentation fault (core dumped) -> 非法内存访问。
1、如何在程序中寻找段错误?
段错误不是语法错误,所以在编译时不会提示出错,只有等到运行时才会提示出现段错误,但是段错误不会提示在哪一行,可以通过printf()函数来寻找段错误位置,只要发生段错误,那么程序就会马上结束。
例子:
printf("11111!\n");
xxxx;
printf("22222!\n");
yyyy;
printf("33333!\n");
zzzz;
执行:
11111!
22222!
Segmentation fault (core dumped) -> 说明段错误是出现"yyyy;"
2、找到段错误之后,怎么处理?
一般段错误都是与指针指向有关,找到段错误准确那一行就打印对应指针的值。
八、void* 通用类型的指针?
1)void *指向一个什么类型的数据?是指向一个void型变量吗?
例子:
void a; -> 没有void型变量
void *p = &a;
void b = *p;
void *类型指针代表该指针指向任何类型,而且C语言没有void型变量。
2)把特定类型赋值给void *代表含义?
int a; --> 买了一个商场(int)
int *p = &a; --> p装者商场的地址 p的数据类型是 商场*(int*)
void *pa = p; --> p的地址赋值给pa,pa是void *类型,pa可以指向任何类型的数据
含义:本身p已知指向int类型,赋值给pa,pa还是指向那片区域,但是那片区域是什么类型的数据,就不知道了!
3)把void *赋值给特定类型含义?
int a; --> 买了一个商场(int)
int *p = &a; --> p装者商场的地址 p的数据类型是 商场*(int*)
void *pa = p; --> p的地址赋值给pa,pa是void *类型,pa可以指向任何类型的数据
int *pb = pa; --> pa指向未知类型,但是赋值给pb之后,由于pb是int*,所以该地址上数据变量int *
4)解引用
可以解引用特定的类型,不可以解引用void *,如果需要解引用void *,必须先将void *转换到特定的数据类型,再解引用。
例子:
int a = 100;
int *p = &a;
printf("%d\n",*p); //100
void *pa = p;
printf("%d\n",*pa); //编译出错
void *pa = p;
printf("%d\n",*(int *)pa); //100
void *pa = p;
int *pb = pa; 结论: 把void *赋值给某种特定的类型,其实就是强转为该种类型。
printf("%d\n",*pb); //100
九、指针的运算
例子:指针加法
int a;
int *pa = &a;
pa+1 -> 向上移动1个单位,每一个单位等于多少个字节,就要看这个指向的内容占用多少个字节。
char b;
char *pb = &b;
pb+2 -> 向上移动2个单位,每一个单位等于1字节,一共是2个字节。
例子:指针减法
int c;
int *pc = &c;
pc-pb //编译出错,不同类型之间指针不可以相减。
pc-pa //编译通过,可以求出两个指针之间相差单位数。
十、数组的运算
1、由于数组的名字就是数组首元素地址,一旦申请空间后,该数组的地址就不能改变,所以数组名字是一个常量
#include <stdio.h>
int main()
{
int A[3];
printf("A = %p\n",A); //0xbfe396c4
A = 0xbfe10104; -> 数组名是一个常量,不能再赋值别的地址给数组名。
A[0] = 100;
A[0] = 200; -> 数组本身是一个变量,可以随时对成员赋值。
return 0;
}
2、数组名字是首元素地址,那么首元素的地址是什么类型?
int A[3] -> 每一个成员都是int类型的 -> 首元素也是int类型 -> 首元素的地址就是int *
例子:
int A[3] = {100,200,300};
A -> 首元素的地址
A+1 -> 将首元素的地址往上移动1个单位,由于首元素是int类型,往上移动4个字节。
A+2 -> 将首元素的地址往上移动2个单位,由于首元素是int类型,往上移动8个字节
*(A+0) -> 解引用首元素的地址 -> 首元素 -> A[0]
*(A+1) -> 解引用首元素往上移动1个单位的地址 -> 第二个元素 -> A[1]
*(A+2) -> 解引用首元素往上移动2个单位的地址 -> 第三个元素 -> A[2]
结论:数组下标其实首元素往上移动的单位数
A[n] = *(A+n) 非常非常非常重要!
3、加法交换律
A[n] = *(A+n) = *(n+A) = n[A]
#include <stdio.h>
int main()
{
int A[3] = {100,200,300};
printf("A[0] = %d\n",A[0]); //100
printf("*(A+0) = %d\n",*(A+0)); //100
printf("*(0+A) = %d\n",*(0+A)); //100
printf("0[A] = %d\n",0[A]); //100
return 0;
}
十一、复杂指针定义?
简单指针 -> 指针指向基本的数据类型,例如: int* char* double*
复杂指针 -> 指针指向非基本数据类型,例如: 指针/数组/函数/结构体
1、指向指针变量的指针 -> 二级指针?
int a = 100;
int *pa = &a;
int **p = &pa;
例子:
#include <stdio.h>
int main()
{
int a = 100;
int *pa = &a;
int **p = &pa;
printf("pa = %p\n",pa);
printf("p = %p\n",p);
printf("%p\n",*p);
printf("%d\n",**p);
return 0;
}
练习:
1、设有定义:int a,*pa=&a;以下scanf语句中能正确为变量a读入数据的是( A ) scanf("%d",&a); pa = &a
A) scanf(“%d”,pa); B) scanf(“%d”,a);
C) scanf(“%d”,&pa); D) scanf(“%d”,*pa);
2、若有以下定义和语句
#include <stdio.h>
int a=4,b=3,*p,*q,*w;
p=&a;q=&b;w=p;q=NULL;
则以下选项中错误的语句是( A )
A) *q=0; B) w=p; C)*p=a; D) *p=*w;
3、有以下程序
main()
{
int a=7,b=8,*p,*q,*r;
p=&a;q=&b;
r=p;p=q;q=r;
printf(“%d,%d,%d,%d\n”,*p,*q,a,b);
}
程序运行以后的输出结果是( C )
A)8,7,8,7 B) 7,8,7,8
C)8,7,7,8 D) 7,8,8,7
4、程序中对fun函数有如下说明
void *fun();
此说明的含义是( C )
A) fun函数无返回值
B) fun函数的返回值可以是任意的数据类型
C) fun函数的返回值是无值型的指针类型
D) 指针fun指向一个函数,该函数无返回值
5、有以下程序
int *f(int *x,int *y)
{
if(*x<*y) return x;
else return y;
}
main()
{
int a=7,b=8,*p,*q,*r;
p=&a;q=&b;
r=f(p,q);
printf(“%d,%d,%d\n”,*p,*q,*r);
}
执行后输出结果是( B )
A) 7,8,8 B) 7,8,7 C) 8,7,7 D) 8,7,8