目录
编写一个函数,生成10个随机数,并使用表示指针的数组名(一个数组元素的地址)来返回它们
指针的基本介绍
- 指针是C语言的精华,也是C语言的难点
- 指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量
- 获取变量的地址,用&
- 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值
- 获取指针类型所指向的值,使用*ptr
什么是指针
- 指针是一个变量,其值为另一个变量的地址,即内存地址的直接地址。在使用指针存储其他变量地址之前,对其进行声明
指针的算术运算
- 数组在内存中是连续分布的
- 当对指针进行++时,指针会按照它指向的数据类型字节数大小增加,每++,就增加4个字节
#include<stdio.h>
const int MAX = 3;//常量
int main () {
int var[] = {10, 100, 200}; // int 数组
int i, *ptr; // ptr 是一个 int* 指针
ptr = var; // ptr 指向了 var 数组的首地址
for ( i = 0; i < MAX; i++) {
printf("var[%d]的地址= %p \n", i, ptr );
printf("存储值:var[%d] = %d\n\n", i, *ptr );
ptr++;// ptr = ptr + 1(1个int字节数); ptr 存放值+4字节(int)
}
getchar();
return 0;
}
int main () {
int var[] = {10, 100, 200};
int i, *ptr;
ptr = var; // 将 var 的首地址 赋给 ptr
ptr += 2; // ptr 的存储的地址 + 2个int的字节 (8个字节)
printf("var[2]=%d \nvar[2]的地址=%p \nptr存储的地址=%p \nptr指向的值=%d",
var[2], &var[2], ptr, *ptr);
getchar();
return 0;
}
const int MAX = 3;//常量
int main () {
int var[] = {10, 100, 200}; // int 数组
int i, *ptr; // ptr 是一个 int* 指针
/* 指针中最后一个元素的地址 */
ptr = &var[MAX-1]; // &var[2]
for ( i = MAX; i > 0; i--) {// 反向遍历
printf("ptr存放的地址=%p\tvar[%d]的地址=%p\n", ptr,i,&var[i-1]);
printf("存储值:var[%d] = %d\n\n", i-1, *ptr );
ptr--; // ptr = ptr - 1(1个int的字节数 [4个字节])
}
getchar();
return 0;
}
指针的比较
#include <stdio.h>
int main () {
int var[] = {10, 100, 200};
int *ptr;
ptr = var;//ptr 指向var 首地址(第一个元素)
if(ptr == var[0]) {//错误,类型不一样 (int *) 和 (int )
printf("ok1");//不输出
}
if(ptr == &var[0]) { // 可以
printf("\nok2"); //输出
}
if(ptr == var) { //可以
printf("\nok3"); //输出
}
if(ptr >= &var[1]) { //可以比较,但是返回false
printf("\nok4");//不会输出
}
getchar();
return 0;
}
const int MAX = 3;
int main () {
int var[] = {10, 100, 200};
int i, *ptr;
ptr = var;
i = 0;
while ( ptr <= &var[MAX - 2] ){//&var[1]
printf("Address of var[%d] = %x\n", i, ptr );//16进制输出
printf("Value of var[%d] = %d\n", i, *ptr );
ptr++;
i++;
} //会输出 10 , 100
getchar();
return 0;
}
指针数组
#include <stdio.h>
const int MAX=3;
int main (){
//指针数组
int var[] = {10, 100, 200};
int i, *ptr[3];
for ( i = 0; i < MAX; i++){
ptr[i] = &var[i]; /* 赋的值为整数的地址 */
}
for ( i = 0; i < MAX; i++){ //指针数组来获取各个值
printf("Value of var[%d] = %d \tptr[%d]本身的地址=%p\t ptr[%d]保存的地址=%p\t var[%d]的地址=%p\n",
i, *ptr[i] , i, &ptr[i],i,ptr[i],i,&var[i]); // 10, 100, 200
}
getchar();
return 0;
}
定义一个指向字符的指针数组来存储字符串列表
(定义一个指针数组,该数组的每个元素,指向的是一个字符串)
#include <stdio.h>
void main() {
//定义一个指针数组,该数组的每个元素,指向的是一个字符串
//当输出数组所指字符串直接是books[i]
char *books[] = {
"三国演义",
"西游记",
"红楼梦",
"水浒传"
};
char * pStr = "abc";
int strLen = sizeof(pStr);
//遍历
int i, len = 4;
for(i = 0; i < len; i++) {
printf("\nbooks[%d] 指向字符串 = %s,books[%d]的地址 = %p\n", i, books[i],i,&books[i]);
printf("pStr指向的内容 = %s 地址 = %p\n\n",pStr,pStr);
printf("pStr指针的长度为%d\n\n", strLen);
}
getchar();
}
- 为什么books[]指针字符数组每一个地址长度都为4?
这是指针的长度,不是数组的长度
- 若求的是books[]的长度,则可以使用sizeof()函数中的形参为books.即sizeof(books)
多重指针
基本介绍
指向指针的指针是一种多级间接寻址的方式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
- 一个指向指针的指针变量在变量名前放置两个星号。
int **ptr//ptr的类型是int **
练习
#include<stdio.h>
void main() {
int var;
int* ptr;
int** pptr;
var = 3000;
ptr = &var;//var变量的地址赋给ptr
pptr = &ptr;//将ptr的地址,赋给pptr
printf("var的地址 = %p, var = %d\n\n", &var, var);
printf("ptr的本身地址 = %p, ptr存放的地址 = %p, *ptr = %d\n\n", &ptr, ptr, *ptr);
printf("pptr的本身地址 = %p, pptr存放的地址 = %p, **pptr = %d\n", &pptr, pptr, **pptr);
getchar();
}
传递指针(地址)给函数
//指针传递给函数
//数组默认也是指针传递
#include<stdio.h>
void test2(int *p); //函数声明,接收int *
void main() {
int num=90;
int *p = # //将 num 的地址赋给 p
test2(&num); //传地址
printf("\nmain() 中的num=%d", num); // num = 91
test2(p); //传指针
printf("\nmain() 中的num=%d", num);// num = 92
getchar();
}
void test2(int *p) {
*p += 1; //*p 就访问 num的值
}
传数组给指针变量
数组名本身就代表该数组首地址,因此传数组的本质就是传地址
#include <stdio.h>
/* 函数声明 */
double getAverage(int *arr, int size); //函数声明
double getAverage2(int *arr, int size); //函数声明
int main ()
{
/* 带有 5 个元素的整型数组 */
int balance[5] = {1000, 2, 3, 17, 50};
double avg;
/* 传递一个指向数组的指针作为参数 */
avg = getAverage( balance, 5 ) ;
/* 输出返回值 */
printf("Average value is: %f\n", avg );
getchar();
return 0;
}
//说明
//1. arr 是一个指针,
double getAverage(int *arr, int size){
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i){
// arr[0] = arr + 0
// arr[1] = arr + 1个int字节(4)
// arr[2] = arr + 2个int字节(8)
//...
sum += arr[i];// arr[0] =>数组第一个元素的地址
//此时函数的指针指向的地址(保存的地址)并不会改变,而是直接通过下标取值
printf("\narr存放的地址=%p ", arr);
}
avg = (double)sum / size;
return avg;
}
#include <stdio.h>
/* 函数声明 */
double getAverage(int *arr, int size); //函数声明
double getAverage2(int *arr, int size); //函数声明
int main ()
{
/* 带有 5 个元素的整型数组 */
int balance[5] = {1000, 2, 3, 17, 50};
double avg;
/* 传递一个指向数组的指针作为参数 */
avg = getAverage2( balance, 5 ) ;
/* 输出返回值 */
printf("Average value is: %f\n", avg );
getchar();
return 0;
}
//说明
//1. arr 是一个指针,
double getAverage2(int *arr, int size){
int i, sum = 0;
double avg;
for (i = 0; i < size; ++i){
sum += *arr;
printf("\narr存放的地址=%p ", arr);
arr++; // 指针的++运算, 会对arr 存放的地址做修改
}
avg = (double)sum / size;
return avg;
}
如果在getVerage()函数中,通过指针修改了数组的值,那么main函数的balance数组也会发生相应变化。getVerage函数中的指针,指向的就是main函数的数组
返回指针的函数
C语言允许函数的返回值是一个指针(地址),这样的函数称为指针函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
char *strlong(char *str1, char *str2){ //函数返回的char * (指针)
printf("\nstr1的长度%d str2的长度%d", strlen(str1), strlen(str2));
if(strlen(str1) >= strlen(str2)){
return str1;
}else{
return str2;
}
}
int main(){
char str1[30], str2[30], *str; // str 是一个指针类型,指向一个字符串
printf("\n请输入第1个字符串");
//gets(str1);//和scanf()函数的功能一致
scanf("%s", str1);
getchar();
printf("\n请输入第2个字符串");
gets(str2);
str = strlong(str1, str2);
printf("\nLonger string: %s \n", str);
printf("\nstr指针本身的地址为: %p \n", &str);
printf("\nstr指针指向的地址为: %p \n", str1);
printf("\nstr1数组的地址为: %p \n", str1);
printf("\nstr2数组的地址为: %p \n", str2);
getchar();
return 0;
}
指针函数的注意事项和细节
- 用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据。
- 函数运行结束后会销毁该函数所有的局部数据,但是这所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存。
- C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为static变量
#include <stdio.h>
int *func(){
//int n = 100;//局部变量, 在func 返回时,就会销毁
static int n = 100; // 如果这个局部变量是 static 性质的,那么n 存放数据的空间在静态数据区
return &n;
}
int main(){
int *p = func(); //func 返回指针
printf("当前返回的指针指向的值为%d", *p);
int n;
//printf("okoook~~"); //可能是使用到 局部变量 int n = 100 占用空间
//printf("okoook~~");
//printf("okoook~~");
n = *p;
printf("\nvalue = %d\n", n);// 思考,当直接int n = 100时是否能够输出100? 不一定
//当用static修饰时,就能直接输出100
getchar();
return 0;
}
编写一个函数,生成10个随机数,并使用表示指针的数组名(一个数组元素的地址)来返回它们
#include <stdio.h>
#include <stdlib.h>
#include<time.h>
//编写一个函数,返回一个一维数组
int * f1() {
static int arr[10]; //必须加上static ,让arr 的空间在静态数据区分配
int i = 0;
srand((unsigned)time(NULL));//随机数需要的,同时还有<time.h>头文件
for (i = 0; i < 10;i++) {
//arr[i] = rand();//伪随机数
arr[i] = rand();//真正随机数
}
return arr;
}
void main() {
int *p;
int i;
p = f1(); //p 指向是在 f1 生成的数组的首地址(即第一个元素的地址)
for ( i = 0; i <10; i++) {
printf("\n%d", *(p+i));
}
getchar();
}
函数指针(指向函数的指针)
基本介绍
- 一个函数总是占用一段连续的内存区域,函数名在表达式中有时会被转换为函数所在内存区域的首地址,这和数组名非常类似
- 把函数的这个首地址(入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数,就叫做函数指针
函数指针定义
returnType (*pointerName)(param list);
- returnType为函数返回值类型
- pointerName为指针名称
- param list为函数参数列表
- 参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称
- 注意()的优先级高于*,第一个括号不能省略,如果写作
returnType *pointerName (param list);
就成了函数原型,它表明函数的返回值类型为returnType *
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//说明
//1. max 函数
//2. 接收两个int ,返回较大数
int max(int a, int b){
return a>b ? a : b;
}
int main(){
int x, y, maxVal;
//说明 函数指针
//1. 函数指针的名字 pmax
//2. 第一个int 表示 该函数指针指向的函数是返回int 类型
//3. (int, int) 表示 该函数指针指向的函数形参是接收两个 int
//4. 在定义函数指针时,也可以写上形参名 int (*pmax)(int x, int y) = max;
int (*pmax)(int x, int y) = max; //pmax函数指针指向的就是max函数
printf("Input two numbers:");
scanf("%d %d", &x, &y);
// (*pmax)(x, y) 通过函数指针去调用 函数max
// 调用方式 2 pmax(x,y)
//maxVal = pmax(x,y);同样可以输出正确
maxVal = (*pmax)(x, y);
printf("\nmax函数的首地址为%p\n", &max);
printf("\nMax value: %d pmax保存的地址为%p pmax本身的地址=%p\n", maxVal, pmax, &pmax);
getchar();
getchar();
return 0;
}
回调函数
-
函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数
-
回调函数就是由别人的函数执行时调用自己传入的函数
#include <stdlib.h>
#include <stdio.h>
// 回调函数
//1. int (*f)()
//2. f 就是 函数指针 , 它可以接收的函数是 (返回 int ,没有形参的函数)
//3. f 在这里被 initArray 调用,充当了回调函数角色
void initArray(int *array, int arraySize, int (*f)()) {
int i ;
//循环10
for ( i=0; i<arraySize; i++) {
array[i] = f(); //通过 函数指针调用了 getNextRandomValue 函数
}
}
// 获取随机值
int getNextRandomValue() {
return rand();//rand 系统函数, 会返回一个随机整数
}
int main() {
int myarray[10],i; //定义一个数组和 int
//说明
//1. 调用 initArray 函数
//2. 传入了一个函数名 getNextRandomValue (地址), 需要使用函数指针接收
initArray(myarray, 10, getNextRandomValue);
//输出赋值后的数组
for(i = 0; i < 10; i++) {
printf("%d\n", myarray[i]);
}
printf("\n");
getchar();
return 0;
}
输出值为10个整数,也就是一个数组的形式
指针的注意事项和细节
- 指针变量存放的是地址,指针的本质就是地址
- 变量声明的时候,如果没有确切的地址赋值,为指针变量赋一个NULL值
- 赋为NULL值得指针被称为空指针,NULL指针是一个定义在标准库<stdio.h>中值为零的常量
int *p = NULL; // p 空指针
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83261.html