C语言指针_06

地址和指针的概念:

内存区的每一个字节有一个编号,这就是“地址”。如果在程序中定义了一个变量,对程序进行编译时,系统就会给这个变量分配内存单元。

在C语言中,对变量的访问有两种方式:

1)直接访问 例如:a = 6;

2)间接访问 例如:scanf("%d", &a);

调用函数时,把变量a的地址传给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到a变量中。

1     /*
2      *:取值操作符;
3      &:取址操作符
4      */
5     int i = 66;
6     int *pointer; // 注意:此处*,不是取值,而是定义指针变量!!
7     pointer = &i; // &取址操作符
8     printf("%d\n", *pointer); // *pointer 取值操作符

指针与指针变量

知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把变量的地址称为该变量的“指针”。

C语言中可以定义一类特殊的变量,这些变量专门用来存放变量的地址,称为指针变量

在定义指针变量是要注意两点:

1)指针变量前面的*,表示该变量的类型为指针型变量,其一般形式为:类型说明符 *变量名;

其中,*表示这个一个指针变量,变量名即为定义的指针变量名,类型说明符表示该指针变量所指向的变量的数据类型。

2)定义指针变量是“必须”指定基类型。注意:只有整型变量的地址才能放到指向整型变量的指针变量中;例如:

1    // 错误!!
2     float a;
3     int *pointer_1;
4     pointer_1 = &a;

指针变量只能存放地址!!

 1     // 通过指针变量访问整型变量
 2     int a = 88, b = 66;
 3     int *pointer_1, *pointer_2;
 4     pointer_1 = &a;
 5     pointer_2 = &b;
 6
 7     printf("%d %d\n", a, b);
 8     printf("%d %d\n", *pointer_1, *pointer_2);
 9     /*
10      输出结果:
11      88 66
12      88 66
13      */

关于“&” “*”说明:‘&’‘*’两个运算符的优先级别相同,但按自右向左方向结合;

定义一个指针变量 int *p;  p = &a[i]

引用一个数组元素,可以用:

1)下标法:如 a[i]

2)指针法:如*(a + i) 或 *(p + i)

注意:数组名即“翻译成数组的第一个元素的地址”(编译器会把数组名编译为地址,数组第一个元素的地址)

对”&“和”*“运算符再做些说明:

1     int a = 100;
2     int *pointer_1;
3     pointer_1 = &a;

1)&*pointer_1 的含义是什么呢 ?

解析:‘&’和‘*’两个运算符的优先级别相同,但它俩都是右结合性(自右向左方向结合),因此先进行 *pointer_1的运算,即:*pointer_1 = a;在执行&运算,即:&a,为变量a的地址。

2)*&a 的含义是什么?

先进行 &a,为变量a的地址;在*运算,即取值运算,最终结果就是变量a;

3)(*pointer_1) ++ 相当于  a ++;  括号是必要的,因为’++‘ 和 ’*‘ 同一优先级别,而且也是右结合性。

数组,一个数组包含若干个元素,每个数组元素在内存中都占用存储单元,他们都有相应的地址,地址是连续的。

用数组名作为函数参数

1 // 编译时,是将arr按指针变量处理的,相当于将函数首部写成
2 // input(int *arr, 10);
3 // void input(int arr[], int n) 等价于 void input(int *arr, int n)
4 void input(int arr[], int n) {
5     printf("do something");
6 }

需要说明的是:C语言调用函数时,虚实结合的方法都是采用”值传递“方式,当用变量名作为函数参数时传递的是变量的值;当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传值的值是地址,所以要求形参为指针变量。

关于数组的一个常见错误:

 1 char *c_strcpy (char destination[],const char source[]);
 2 int main(void) {
 3     char str1[] = "this is a very long string";
 4     char str2[] = "this is a short string";
 5     strcpy(str2, str1);
 6     puts(str2);
 7     return 0;
 8 }
 9
10 char *c_strcpy (char destination[], const char source[]) {
11     int size_of_array = sizeof source / sizeof source[0];
12     for (int i = 0; i < size_of_array; i++) {
13         destination[i] = source[i];
14     }
15     return destination;
16 }

出现错误的是第 11 行代码。我们知道,数组作为函数的参数,在本函数中 source 只是一个指针(地址,系统在本函数运行时,是不知道a所表示的地址有多大的数据存储空间,这里只是告诉函数:一个数据存储空间首地址),所以,sizeof source 的结果是指针变量 source 占内存的大小。一般在32位机上是4个字节。source[0] 是 int 类型,sizeof source 也是4个字节,所以,结果永远是1。
结论:获得数组长度,只能在数组定义所在的代码区中获取数组长度,才能获取正确的数组长度。

正确代码:

 1 char *c_strcpy (char destination[], const char source[], int len);
 2 int main(void) {
 3     char str1[] = "this is a very long string";
 4     char str2[] = "this is a short string";
 5     int len = sizeof(str1) / sizeof(str1[0]);
 6     c_strcpy(str2, str1, len);
 7     puts(str2);
 8     return 0;
 9 }
10
11 char *c_strcpy (char destination[], const char source[], int len) {
12     for (int i = 0; i < len; i++) {
13         destination[i] = source[i];
14     }
15     return destination;
16 }

倒序输出数组中的值:

 1 int main(int argc, const char * argv[]) {
 2     int a[] = {34, 56, 89, 87, 1, 34, 67};
 3     int count = sizeof(a)/sizeof(int);
 4     int i;
 5     reverse_pointer(a, count);
 6 //    reverse_arr(a, count);
 7     for (i = 0; i < count; i ++) {
 8         printf("%d\n", a[i]);
 9     }
10     return 0;
11 }
12
13 // 方法一:倒序输出数组中的值(指针作为参数)
14 void reverse_pointer(int *x, int n) {
15     int *p, *i, *j, temp, m;
16     m = (n - 1) / 2;
17     i = x;          // i指向数组的第一个元素
18     j = x - 1 + n;  // j指向数组的最后一个元素
19     p = x + m;      // p指向数组的中间一个元素,配对
20     for (; i < p; i ++ , j --) {
21         temp = *i;
22         *i = *j;
23         *j = temp;
24     }
25 }
26
27 // 方法二:倒序输出数组中的值(数组作为参数)
28 void reverse_arr(int arr[], int len) { // 形参arr是数组名,在此处仅是指针
29     int temp, i, j, m;
30     m = (len -1 ) / 2;
31     for (i = 0; i < m; i ++) {
32         j = len - 1 - i;
33         temp = arr[i];
34         arr[i] = arr[j];
35         arr[j] = temp;
36     }
37 }
 1 // 找出数组中的最大值和最小值
 2 void max_min_value(int arr[], int n) {
 3     int *p, *array_end;
 4     array_end = arr + n;
 5     int max, min;
 6     max = min = *arr;
 7     for (p = arr + 1; p < array_end; p ++) {
 8         if (*p > max) {
 9             max = *p;
10         } else if (*p < min) {
11             min = *p;
12         }
13     }
14 }

归纳起来,如果有一个实参数组,想在函数中改变此数组中元素的值,实参与形参的对应关系有以下4种情况:

1) 形参和实参都用数组名,如

1 void func(int x[], int n) {
2     printf("do something");
3 }
4
5 void main(void) {
6     int a[10];
7     func(a, 10);
8 }

2) 实参用数组名,形参用指针变量,如

1 void func(int *p, int n) {
2     printf("do something");
3 }
4
5 void main(void) {
6     int a[10];
7     func(a, 10);
8 }

3) 实参形参都采用指针变量,如

1 void func(int *p, int n) {
2     printf("do something");
3 }
4
5 void main(void) {
6     int a[10], *p;
7     func(p, 10);
8 }

4) 实参为指针变量,形参为数组名,如

1 void func(int x[], int n) {
2     printf("do something");
3 }
4
5 void main(void) {
6     int a[10], *p;
7     func(p, 10);
8 }

多维数组与指针:

多维数组元素的地址,定义一个二维数组

1 int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};

设二维数组的首行的首地址为2000,则:

一维数组里面存放的是二维数组元素的地址,二维数组里面存放的是二维数组元素的值!

 1     // 定义二维数组,三行四列
 2     int a[3][4] = {0, 1, 2, 3 , 4, 5, 6, 7, 8, 9, 10, 11};
 3
 4     // 打印 a[0][0] 首元素地址
 5     printf("a=%p\n", a);
 6     printf("*a=%p\n", *a);
 7     printf("a[0]=%p\n", a[0]);
 8     printf("&a[0]=%p\n", &a[0]);
 9     printf("&a[0][0]=%p\n", &a[0][0]);
10
11     printf("\n");
12
13     // 打印 a[1][0] 的地址,第二行
14     printf("a+1=%p\n", a + 1);
15     printf("*(a+1)=%p\n", *(a + 1));
16
17     printf("\n");
18
19     // 打印 a[1][1] 的值
20     printf("*(a[1]+1)=%d\n", *(a[1]+1));
21     printf("*(*(a+1)+1)=%d\n", *(*(a+1)+1));
22
23     // 把二维数组 a 分解为一维数组 a[0],a[1],a[2]之后,设 p 为指向二维数组的指针变量,可定义为:
24     int (*p)[4];
25     // 它表示 p 是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组 a[0],其值等于 a,a[0] 或 &a[0][0]
26
27     // 而 p + i 则指向一维数组 a[i]
28
29     // 结论: *(p + i) + j 是二维数组 i 行 j 列的元素的地址
30     // 而 *(*(p + i) + j) 是 i 行 j 列元素的值
31
32     // 二维数组指针变量说明的一般形式:
33     类型说明符 (*指针变量名)[长度]
34
35     // 其中“类型说明符”为所指数组的数据类型。
36     //    “*”表示其后的变量是指指针类型。
37     //    “长度”表示二维数组分别为多个一维数组时,一维数组的长度,也就是二维数组的列数
 1     // 用指针输出二维数组的值
 2     int a[3][4] = {0, 1, 2, 3 , 4, 5, 6, 7, 8, 9, 10, 11};
 3     int (*p)[4];
 4     int i, j;
 5     p = a;
 6     for (i = 0; i < 3; i ++) {
 7         for (j = 0; j < 4; j ++) {
 8             printf("%2d", *(*(p + i) + j));
 9         }
10     }
时间: 2024-10-17 13:40:12

C语言指针_06的相关文章

C语言指针

C语言指针 指针是什么? 指针也是一个变量,但是这个变量存储的是一个地址,指向一个存储单元.我们可以根据指针(地址)找到他所指的变量,并对这个变量进行操作(赋值等).通过C里面的取地址符号&和取所指内容的符号*可以很方便的表示指针以及指针所指的变量之间的关系 /* ============================================================================ Name C语言指针 : text.c Author : Version : C

C语言--指针(一)

#import <Foundation/Foundation.h> //交换函数 void swap(int x, int y) { printf("x=%d,y=%d",x,y); int temp = 0; temp = x; x = y; y = temp; printf("x=%d,y=%d",x,y); } // void swap2(int *x , int *y) { //不改地址,改地址对应的值 printf("x=%d,y=%

C语言指针讲解

指针是C语言的一种数据类型,类似于C中的其他类型,例如int ,char 等.既然指针是一种类型,当我们定义该类型变量,该类型变量就称为指针变量. C中有了指针就有了指向.指向:指针变量指向本身保存的内容(地址或指针)所表示的内存单元. C语言指针是一个特殊的变量,其里面储存的数值被解释成为内存里的一个地址. 在计算机中,所有对数据的操作,都是通过其地址进行的,指针让程序的效率更高,代码更少. 在学习指针的时间,一定要理解并区分指针的四个感念:指针的类型,指针所指向的类型,指针的值,指针本身所占

[转]C 语言指针的使用

第一章 指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址. 要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区.让 我们分别说明. 先声明几个指针放着做例子: 例一: (1)int *ptr; (2)char *ptr; (3)int **ptr; (4)int (*ptr)[3]; (5)int *(*ptr)[4]; 如果看不懂后几个例子的话,请参阅我前段时间贴出的文?lt;<如何

C语言指针的陷阱

C语言指针的陷阱   分类: C/Cpp 转自:http://blog.csdn.net/porscheyin/article/details/3461670 "C语言诡异离奇,陷阱重重,却获得了巨大成功!"--C语言之父Dennis M.Ritchie.Ritchie大师的这句话体现了C语言的灵活性以及广泛的使用,但也揭示了C是一种在应用时要时刻注意自己行为的语言.C的设计哲学还是那句话:使用C的程序员应该知道自己在干什么.有时用C写的程序会出一些莫名其妙的错误,看似根源难寻,但仔细

C语言指针占用字节

指针即为地址,存的是变量的地址,在同一架构下地址长度都是相同的(cpu的最大寻址内存空间),所以不同类型的指针长度都一样. 指针占用几个字节跟语言无关,而是跟系统的寻址能力有关,16为地址指针即为2个字节,32位系统是4个字节,64位系统为8个字节. 用sizeof求长度: #include<stdio.h> void main(void) { printf("%d %d %d\n",sizeof(int),sizeof(double),sizeof(char)); } 在

关于c语言 指针课堂随笔

右左法则:首先从最里面的圆括号看起,然后往右看,再往左看.每当遇到圆括号时,就应该掉转阅读方向.一旦解析完圆括号里面所有的东西,就跳出圆括号.重复这个过程直到整个声明解析完毕 1.指针的判断应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识符,但未定义的标识符只会有一个 实例: int(*func)(int*p)    先从标识符func找起往右走  碰到圆括号 反转 从左走 func的指针  再往右碰到圆括号这是个函数    func是指

c语言指针学习

前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水,食品,你能告诉还有没有问题的吗?不知大难至,世人依旧忙.祸福相依,危中有机.那些高级生命,出于慈悲,会救渡我们,但是你要去思考,去发现机缘. 最近较闲,没事就学点基础知识,整天在上层晃,感觉晕的厉害,接地气.关于指针我上学的时候学过一点,我的老师说"指针很难呢",当时以为这老师挺谦虚的.后

C语言指针,你还觉得难吗?

在研究式学习-c语言程序设计指针式这样介绍的: 内存中存储变量的第一个单元的地址 称为指针,存放指针的变量称为指针变量: 变量的访问方式有: 直接访问方式:按变量名查找其首地址 间接访问方式:通过指针访问 int swap(int p,int q){ int temp; temp=p; p=q; q=temp;} 现在理解了这个函数为什么要用指针了: 因为C语言中实参变量和形参变量之间的数据传递是单向的值传递方式,虽然运用指针被调函数不能改变实参指针变量的值,但可以改变实参指针变量指向的变量的值