C语言基础学习day08
指针
地址和指针的概念
内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。
在C语言中,对变量的访问有两种方式,直接访问和间接访问。
关于直接访问和间接访问
直接访问如:a=5; 系统在编译时,已经对变量分配了地址,例如,若变量a分配的地址是2000,则该语句的作用就是把常数5保存到地址为2000的单元。
间接访问如:scanf("%d",&a); 调用函数时,把变量a的地址传递给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到a变量中。
初识指针
在C语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量 int *i_pointer
用来存放整型变量 i 的地址。 可以通过语句:i_pointer =&i;
将i的地址(2000)存放到i_pointer中。这时, i_pointer的值就是(2000) ,即变量i所占用单元的起始地址。
要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer ,从中取出i的地址(2000),然后取出i的值3。
初识两个操作符“*”和“&”
*:叫做取值操作符
&:而这玩意叫做取址操作符
#include<stdio.h> void main() { int i = 2000; int *pointer; pointer = &i; printf("%d\n", pointer); //打印地址6487572 }
#include<stdio.h> void main() { int i = 2000; int *pointer; pointer = &i; printf("%d\n", *pointer);//打印这个地址所储存的值2000 }
指针和指针变量
知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把变量的地址称为该变量的“指针” 。 指针指的是地址
C语言中可以定义一类特殊的变量,这些变量专门用来存放变量的地址,称为指针变量。
注意:指针变量的值(即指针变量中存放的值)是地址(即指针)。请区分“指针”和“指针变量”这两个概念。
定义指针变量时要注意两点
一、指针变量前面的“*”,表示该变量的类型为指针型变量。
其一般形式为: 类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
例如: float *pointer_1; 指针变量名是pointer_1 ,而不是* pointer_1
二、在定义指针变量时必须指定基类型。(指针指向变量的类型)
需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的∶
float a;
int * pointer_1;
pointer_1=&a; /*将float型变量的地址放到指向整型变量的指 针变量中,错误 */
指针变量的引用 “&”
请牢记,指针变量中只能存放地址(指针), 不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量,否则编译器也会把该值当成一个地址来处理。
C语言中提供了地址运算符&来表示变量的地址。 其一般形式为:
&变量名;
如&a表示变量a的地址,&b表示变量b的地址。当然,变量本身必须预先声明。
#include<stdio.h> void main() { int a,b; int *pointer_1, *pointer_2; a=100; b=10; pointer_1 = &a; pointer_2 = &b; printf("%d,%d\n",a,b); printf("%d,%d\n",*pointer_1, *pointer_2); }
对“&”和“*”运算符再做些说明
如果已执行了语句 pointer_1=&a;
(1)&* pointer_1的含义是什么?
“&”和“*”两个运算符的优先级别相同,但按自右而左方向结合,因此先进行* pointer_1的运算,它就是变量a,再执行&运算。 因此,&* pointer_1与&a相同,即变量a的地址。
如果有: pointer_2 =&* pointer_1;
它的作用是将&a(a的地址)赋给pointer_2 ,如果 pointer_2 原来指向b,经过重新赋值后它已不再指向b了,而指向了a。
(2) *&a的含义是什么?
先进行&a运算,得a的地址,再进行*运算。即&a所指向的变量,也就是变量a。
*&a和*pointer_1的作用是一样的,它们都等价于变量a。即*&a与a等价。
(3) (*pointer_1)++相当于a++。
注意括号是必要的,如果没有括号,就成为了*pointer_1++,从附录可知:++和*为同一优先级别,而结合方向为自右而左,因此它相当于*(pointer_1++)。
由于++在pointer_1的右侧,是“后加”,因此先对pointer_1的原值进行*运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。
例题:输入a和b两个整数,按先大后小的顺 序输出a和b。
#include <stdio.h> void main() { int *p1, *p2, *p, a, b; scanf("%d %d", &a, &b); p1 = &a; p2 = &b; if( a < b) { p = p1; p1 = p2; p2 = p; } //此后,p1指向b, p2指向a printf("a = %d, b = %d\n", a, b); printf("max = %d, min = %d\n", *p1, *p2); }
输出结果
题目:对输入的两个整数按大小顺序输 出!这次用函数实现交换功能!
#include <stdio.h> void swap(int *p1, int *p2); void main() { int a, b; int *pointer_1, *pointer_2; scanf("%d %d", &a, &b); pointer_1 = &a; pointer_2 = &b; if(a < b) { swap(pointer_1, pointer_2); //swap实现的是交换…… } printf("\n%d > %d\n", a, b); } void swap(int *p1, int *p2) { int temp; printf("I'm swapping……\n"); printf("Please wait^_^"); temp = *p1; //temp = a; *p1 = *p2; //a = b; *p2 = temp; //b = temp; }
数组与指针
一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。
指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。
所谓数组元素的指针就是数组元素的地址
注意:数组名即“翻译成数组的第一个元素的地址!
例 输出数组中的全部元素
假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法:
(1) 下标法
(2) 通过数组名计算数组元素地址,找出元素的值。
(3) 用指针变量指向数组元素
#include <stdio.h> void main() { int a[10]; int i; int *p; for( i=0; i < 10; i++ ) { scanf("%d", &a[i]); } printf("\n"); for( p=a; p < (a+10); p++ ) { printf("%d", *p); } }
用数组名作函数参数
C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。
将数组a中n个整数按相反顺序存放
数组名做参数
#include <stdio.h> void reverse(int x[],int n); /*形参x是数组名*/ void main() { int i, a[10] = {3, 7, 9, 11, 0, 6, 7, 5, 4, 2}; printf("The original array:\n"); for( i=0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); reverse(a, 10); //作用使得数组重新倒序排放 printf("The array has been inverted:\n"); for( i=0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); } void reverse(int x[], int n) /*形参x是数组名*/ { int temp, i, j, m; m = (n-1)/2; for( i=0; i <= m; i++) { j = n-1-i; //j指向对应的元素 temp = x[i]; x[i] = x[j]; x[j] = temp; } }
指针做参数
#include <stdio.h> void reserve(int *x, int n); /*形参x为指针变量*/ void main() { int i, a[10] = {3, 7, 9, 11, 0, 6, 7, 5, 4, 2}; printf("The original array:\n"); for( i=0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); reserve(a, 10); printf("The array has benn inverted:\n"); for( i=0; i < 10; i++) { printf("%d ", a[i]); } printf("\n"); } void reserve(int *x, int n) /*形参x为指针变量*/ { int *p, temp, *i, *j, m; m = (n-1)/2; i = x; //i指向数组的第一个元素 j = x-1+n; //j指向的是数组的最后一个元素 p = x+m; //指向中间,配对…… for( ; i <= p; i++, j--) { temp = *i; *i = *j; *j = temp; } }
小结
归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
(1) 形参和实参都用数组名,如:
void main()
{
int a[10];
… …… f(a,10);
}
void f(int x[],int n)
{
..............
}
2) 实参用数组名,形参用指针变量。如:
void main()
{
int a[10];
f (a, 10);
}
f (int *a, int n) { …… }
(3)实参形参都用指针变量。例如:
void main()
{
int a[10], *p = a;
f (p , 10); }
void f (int *x, int n) { …… }
(4) 实参为指针变量,形参为数组名。如:
void main() { int a[10], *p = a; ……………… f(p,10); }
f (int x[], int n) { ………… }
字符串与指针
(1) 用字符数组存放一个字符串,然后输出该字符串。
例题一:定义一个字符数组,对它初始化,然 后输出该字符串。
#include <stdio.h> void main() { char string[] = "I love Fishc.com!"; printf("%s\n", string); }
(2) 用字符指针指向一个字符串
例题二:可以不定义字符数组,而定义一个字 符指针。用字符指针指向字符串中的 字符。
#include <stdio.h> void main() { char *string = "I love Fishc.com!I love Fishc.com!"; printf("%s\n", string); }
char *string = "I love Fishc.com!"; 0040D718 mov dword ptr [ebp-4],offset string "I love Fishc.com!" (00422020) ********************************************************** char string[] = "I love Fishc.com!"; 0040D718 mov eax,[string "I love Fishc.com!" (00422020)] 0040D71D mov dword ptr [ebp-14h],eax 0040D720 mov ecx,dword ptr [string "I love Fishc.com!"+4 (00422024)] 0040D726 mov dword ptr [ebp-10h],ecx 0040D729 mov edx,dword ptr [string "I love Fishc.com!"+8 (00422028)] 0040D72F mov dword ptr [ebp-0Ch],edx 0040D732 mov eax,[string "I love Fishc.com!"+0Ch (0042202c)] 0040D737 mov dword ptr [ebp-8],eax 0040D73A mov cx,word ptr [string "I love Fishc.com!"+10h (00422030)] 0040D741 mov word ptr [ebp-4],cx *********************************************************** char string[] = "I love Fishc.com!I love Fishc.com!"; 0040D718 mov ecx,8 0040D71D mov esi,offset string "I love Fishc.com!I love Fishc.co"... (00422fbc) 0040D722 lea edi,[ebp-24h] 0040D725 rep movs dword ptr [edi],dword ptr [esi] 0040D727 movs word ptr [edi],word ptr [esi] 0040D729 movs byte ptr [edi],byte ptr [esi]
字符串中字符的存取方法
对字符串中字符的存取,可以用下标方法,也可以用指针方法!
下标法举例:将字符串a复制为字符串b
#include <stdio.h> void main() { char a[] = "Fishc.com is a good web site!", b[40]; int i; for(i=0; *(a+i) != '\0'; i++) { *(b+i) = *(a+i); } *(b+i) = '\0'; printf("String a is: %s\n", a); printf("String b is: "); for(i=0; b[i] != '\0'; i++) { printf("%c", b[i]); } printf("\n\n"); }
指针方法举例:将字符串a复制为字符串b
#include <stdio.h> void main() { char a[] = "Fishc.com is a good web site!", b[40], *p1, *p2; int i; p1 = a; p2 = b; for( ; *p1 != '\0'; p1++, p2++) { *p2 = *p1; } *p2 = '\0'; printf("String a is: %s\n", a); printf("String b is: "); for(i=0; b[i] != '\0'; i++) { printf("%c", b[i]); } printf("\n"); }
指向函数的指针
用函数指针变量调用函数 可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。
一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。
/****************************/ /* 比较a 和 b的大小,求大值 */ /****************************/ #include <stdio.h> #if(0) void main() { int max(int, int); int a, b, c; scanf("%d %d", &a, &b); c = max(a, b); printf("a = %d, b = %d, max = %d\n\n", a, b, c); } #endif int max(int x, int y) { int z; if( x > y ) { z = x; } else { z = y; } return z; } #if(1) //将 main 函数改写为 #include <stdio.h> void main() { int max(int, int); int (*p)(); int a, b, c; p = max; scanf("%d %d", &a, &b); c = (*p)(a, b); printf("a = %d, b = %d, max = %d\n\n", a, b, c); } #endif
函数指针变量常用的用途之一是把指针作为参数传递到其他函数。
题目: 设一个函数process,在调用它的时候,每次实现不同的功能。(有点类似多态)
输入a和b两个数,第一次调用process时找出a和b中大者,
第二次找出其中小者,
第三次求a与b之和。
/***********************************************************/ /* 设一个函数process,在调用它的时候,每次实现不同的功能。*/ /* 输入a和b两个数,第一次调用process时找出a和b中大者,*/ /* 第二次找出其中小者,第三次求a与b之和。 */ /***********************************************************/ #include <stdio.h> void main() { int max(int, int); /* 函数声明 */ int min(int, int); /* 函数声明 */ int add(int, int); /* 函数声明 */ void process( int, int, int(*fun)() ); /* 函数声明 */ int a, b; printf("Endter a and b: "); scanf("%d %d", &a, &b); printf("max = "); process(a, b, max); printf("min = "); process(a, b, min); printf("sum = "); process(a, b, add); } int max(int x, int y) /* 函数定义 */ { int z; if( x > y ) { z = x; } else { z = y; } return z; } int min(int x, int y) /* 函数定义 */ { int z; if( x < y ) { z = x; } else { z = y; } return z; } int add(int x, int y) { int z; z = x + y; return z; } void process( int x, int y, int(*fun)() ) /* 函数定义 */ { int result; result = (*fun)(x, y); printf("%d\n", result); }
返回指针值的函数
一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。
其概念与以前类似,只是带回的值的类型是指针类型而已。
这种带回指针值的函数,一般定义形式为 类型名 *函数名(参数表列);
例如: int *a(int x,int y);
例题:有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。
#include <stdio.h> void main() { double score[][4] = {{60.0, 70.0, 80.5, 90.5}, {56.0, 89.0, 67.0, 88.0}, {34.2, 78.5, 90.5, 66.0}}; double *search(double(*pointer)[4], int n); double *p; int i, m; printf("Please enter the number of student: "); scanf("%d", &m); printf("The scores of No.%d are: \n", m); p = search(score, m); for( i=0; i < 4; i++) { printf("%5.2f\t", *(p + i)); } printf("\n\n\n"); } double *search(double (*pointer)[4], int n) { double *pt; pt = *(pointer + n); return pt; }
指针函数和函数指针的区别
这两个概念都是简称: 指针函数是指带指针的函数,即本质是一个函数。
函数指针是指向函数的指针变量,因而函数指针本身首先应是指针变量,只不过该指针变量指向函数。
指针数组和指向指针的指针
指针数组的概念: 一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。
一维指针数组的定义形式为 类型名 数组名[数组长度];
例如: int *name[4];
有关指针的数据类型的小结
指针运算小结
一、指针变量加(减)一个整数 例如:p++、p--、p+i、p-i、p+=i、p-=i等。 指针变量赋值 将一个变量地址赋给一个指针变量。
如: p=&a; (将变量a的地址赋给p)
p=array; (将数组array首元素地址赋给p)
p=&array[i];(将数组array第i个元素 的地址赋给p)
p=max;(max为已定义的函数,将ma x的入口地址赋给p)
p1=p2;(p1和p2都是指针变量,将 p2的值赋给p1)
二、指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:p=NULL;
两个指针变量可以相减 如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数
三、两个指针变量比较 若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。
关于 void类型
void真正发挥的作用在于:
(1) 对函数返回的限定;
(2) 对函数参数的限定。 例如:void abc( void );