C语言笔记6--指针与数组

C语言笔记6--指针与数组


总论:

指针是一种保存变量地址的变量。在C语言中,指针的使用非常广泛,原因之一是,指针常常是表达式某个计算的唯一途径,另一个原因是,同其他方法比较起来,使用指针通常可以生产更高效,更紧凑的代码。


指针与地址

指针是一种保存变量地址的变量,它里面存储的数值被解释成为内存里的一个地址。

如:c 的类型char ,并且p是指向c的指针。

char c;
char *p;
p = &c;   /*将把c 的地址赋值给变量p ,我们称p 为“指向”c的指针。*/
一元运算符 & 可用于取一个对象的地址。地址运算符 & 只能 应用于内存中的对象,即变量与数组元素。不能作用于表达式,常量或寄存器retister 变量。
一元运算符 * 是间接寻址或间接引用运算符。当它作用于指针时,将访问 指针所指向的对象
注:指针的声明使用 “*”为了便于记忆。 指针必须指向某种特定的数据类型。由于指针也是变量,所以在程序中可以直接使用。

指针与函数参数
由于C语言是以传值的方式将参数值传递给被调用函数,因此,被调用函数不能直接修改主调函数中变量的值。
指针可以实现这一目标:主调程序将指向所要交换的变量的指针传递给被调用函数,即:
swap(&a,&b);
由于一元运算符&用来取变量的地址,这样&a 就是一个指向变量a的指针。
swap 函数的所有参数都声明为指针,并且通过这些指针来间接访问它们指向的操作数。
void swap(int *pi,int *pj)
{
     int temp;
     temp = *pi;
     *pi = *pj;
     *pj = temp;
}

注:指针参数使得被调用函数能够访问和修改主调函数中对象的值。


数组
1、声明数组的通用格式:

  typeName arrayName[arraySize]
  int a[5]; //(编译器不会检查使用的下标是否有效。)
2、如果没有初始化函数中定义的数组,其元素的值为以前驻留在该内存中的值。(与函数中的变量一样)
3、 sizeof 作用于数组名时,得到的是整个数组中的字节数。作用于元素时,得到的是该元素的字节数。
4、只有定义数组时才能初始化,之后就不行了。可以部分初始化,如:

  int a[5] = {1,2}; //部分初始化时,编译器把其他元素设置为0。
  int b[] = {1,2,3,4,5};  //让编译器计算元素个数。
5、使用列表初始化数组时,可以省略等号(=);大括号内不包含任何内容时,默认所有元素为0;列表初始化禁止缩窄转换。


指针与数组
在C语言中,通过数组下标所能完成的任何操作都可可通过指针来实现,一般来说,用指针编写的程序比用数组下表编写的程序执行速度快,但另一方面,用指针实现的程序理解起来稍微困难一些。

声明:
int a[10];
定义了一个长度为10的数组a。换句话说,它定义了一个由10个对象组成的集合。 

声明:
int *pa;
则声明了一个指向整型对象的指针。

赋值语句:
pa = &a[0];
则可以将指针pa指向数组a的第0个元素。也就是说,pa的值为数组元素a[0]的地址。

赋值语句:
int x = *pa;
将把数组元素a[0]中的内容复制到变量x中。

说明:

如果pa指向数组中的某个特定元素,那么,根据指针运算符的定义,pa + 1 将指向下一个元素,pa + i 将指向pa所指向数组元素之后的第i 个元素。

因此,如果指针pa 指向a[0],那么*(pa + 1)引用的是数组元素a[1]的内容,pa + i 是数组元素a[i]的地址,*(pa + i) 引用的是数组元素a[i]的内容。

无论数组a中的元素的类型或数组长度时什么,上面的结论都成立。


赋值语句:
pa = &a[0];
其中,pa与a具有相同的值,因为数组名所代表的就是该数组最开始的一个元素的地址,所以,赋值语句pa = &a[0]也可以写成:
pa = a;

结论:

对数组元素a[i]的引用也可以写成*(a + i)这种形式。&a[i]和a + i的含义也是相同的。相应地,如果pa 是一个地址,那么,在表达式中也可以在它的后面加下标,pa[i] 与 *(pa + i)是等价的。


注:

1、数组名和指针之间有个不同之处,指针是一个变量,因此,在C语言中,语句pa = a 和pa++都是合法的。

2、 但是数组名不是变量,因此,类似于a = pa 和 a ++ 形式的语发是非法的。

3、注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

4、当把数组名传递给一个函数时,实际上传递的是该数组第一个元素的地址,在被调用函数中,该参数是一个局部变量,因此,数组名参数必须是一个指针。如:

int strlen(char *s)
{
     int n;
	
     for(n = 0; *s != '\0'; s++)
     {
	  n++;
     }
	
     return n;
}
因此,类似于下面这样的函数调用,都可以正确的执行。
void main(void)
{
     char array[] = "hello, would";
     char *ptr = "hello, would";

     strlen("hello, would");
     strlen(array);
     strlen(ptr);
}


地址算术运算
指针的运算规则:
有效的指针运算包括相同类型指针之间的赋值运算;
指针同整数之间的加法或减法运算;
指向相同数组中元素的两个指针间的减法或比较运算;
将指针赋值为0(NULL)或指针与0(NULL)之间的比较运算;

字符串与函数
字符串常量是一个字符数组。
如:"hello, would"

在字符串的内部表示中,字符数组以空字符'\0'结尾,所以,程序可以通过检查空字符找到字符数组的结尾。

字符串常量占据的存存储单元数也因此比双引号奶的字符数大1。

字符串常量最常见的用法就是作为函数参数,如:printf("hello, would\n");

当类似于这样的一个字符串出现在程序中是,实际上是通过字符指针访问该字符串的。

在上述语句中,printf 接受的是一个指向字符数组第一个字符的指针。

注:字符串常量可以通过一个指向其第一个元素的指针访问。
char array[] = "hello, would";  /*定义一个数组*/
char *parray = "hello, would";  /*定义一个指针*/
上述声明中:
array 是一个仅仅足以存放初始化字符串以及空字符'\0'的数组。数组中的单个字符可以进行修改,但array 始终指向同一个存储位置。
parray 是一个指针,其初值指向一个字符串常量,之后它可以被修改以指向其他地址,但试图修改字符串的内容,结果是没有意义的。

解读复杂指针声明

右左法则:

首先从标识符开始阅读,然后往右看,再往左看。每当遇到圆括号时,就应该调转阅读方向。一旦解析完括号里所有的东西,就跳出括号。

重复这个过程直到整个声明解析完毕。

int *a[10]  
// 首先 a 右边是[],说明 a 是一个具有10个元素的数组  
// 其次 a 左边是 int*,说明 a 的元素是 int 类型的指针  
  
int (*a)[10]  
// 首先 a 左边是一个 * 号,说明 a 是一个指针  
// 跳出括号,右边是 [], 说明 a 是一个指向具有10个元素的数组的指针  
// 左边是 int,说明元素的类型是 int  
  
int (*func)(int *p);  
// 首先找到 func,它左边是一个 * 号,这说明 func 是一个指针  
// 然后跳出这个圆括号,先看右边,也是个圆括号,这说明 (*func) 是个函数,而 func 是指向这个函数的指针  
// 这个函数具有 int* 类型的参数,返回值类型为 int  
  
int (*func[5])(int* p)  
// 首先找到 func, 右边是[],说明 func 是一个具有5个元素的数组  
// 其次 func 左边有一个 *,说明 func 的元素是指针,要注意 * 不是修饰func的,而是修饰 func[5]的  
// 跳出这个括号,右边也是一个括号,说明 func 数组的元素是函数类型的指针


指针数组 & 数组指针

指针数组:首先它是一个数组,数组的元素都是指针,也称为"储存指针的数组"。

数组指针:首先它是一个指针,它指向一个数组。也可以理解为"数组的指针。"
int *p1[10]
int (*p2)[10] 

各代表什么?要弄清这个问题,首先要知道[ ]优先级比*要高。

p1先与[ ]结合,构成一个数组定义,数组名为p1,int*修饰的是数组的内容,即数组的每个元素。

p2中()的优先级比[ ]高,所以*号先与P2构成一个指针的定义,指针变量名为P2,int修饰的是数组的内容,即数组的每个元素。数组在这里没有名子。


函数指针 & 指针函数

函数指针:指向函数的指针变量。

指针函数:带指针的函数,也就是返回指针的函数。

char * fun(char* a, char* b)  //定义为 指针函数  
{......  }  
  
int main()    
{    
    char* (*p)(char* p1, char* p2); //定义为 函数指针    
    
    p = &fun; //把函数地址赋给他    
    //p = fun;  //这样写也行  
  
    (*p)("aa", "bb"); //使用函数指针    
    
    return 0;    
}  

指针常量 & 常量指针
const char* p1; //常量指针,指向常量的指针  
char const* p2;  
char* const p3; //指针常量,指针是常量  

a. 可以先把类型名去掉,然后看 const 离谁近,就修饰谁。

b. 也可以const 在 * 左边的为常量指针,const 在 * 右边的为指针常量。


野指针

野指针是怎么造成的?

1. 指针变量被创建而没有初始化。

2. 指针 p 被 free 或者 delete 之后, 没有置为 NULL。 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值