嵌入式linux之高级c语言专题--指针2

本文深入探讨了嵌入式Linux环境下的C语言指针,包括指针与数组的关系、函数传参的原理,特别是传值调用与传址调用的差异。同时,讲解了如何利用const指针保护函数内部不修改变量,并讨论了在函数中如何处理多个返回值的情况。
摘要由CSDN通过智能技术生成

第二部分:嵌入式linux高级c--指针2


1.8指针、数组与sizeof运算符

(1)sizeofC语言的一个运算符(主要sizeof不是函数,虽然用法很像函数),sizeof的作用是用来返回()里面的变量或者数据类型占用的内存字节数

(2)sizeof存在的价值?主要是因为在不同平台下各种数据类型所占的内存字节数不尽相同(譬如int32位系统中为4字节,在16位系统中为2字节···)。所以程序中需要使用sizeof来判断当前变量/数据类型在当前环境下占几个字节

 

1char str[] =hello;  sizeof(str)sizeof(str[0])strlen(str)返回值分别是多少?

#include <stdio.h>
#include <string.h>

int main(void)
{
char str[] = "hello";
printf("sizeof(str) = %d \n",sizeof(str));     // 6 包括字符串的结束符“\0”
printf("sizeof(str[0]) = %d \n",sizeof(str[0])); //1
printf("strlen(str) = %d \n",strlen(str));     //5 不包括字符串的结束符“\0”

return 0;
}

 

题目二:char str[] =hello” ;char *p=str ;  sizeof(p)sizeof(*p)strlen(p)返回值分别是多少?

#include <stdio.h>
#include <string.h>

int main(void)
{
char str[] = "hello";
char *p = str;
printf("sizeof(p) = %d \n",sizeof(p)); //4,相当于sizeof(char *)
printf("sizeof(*p) = %d \n",sizeof(*p));//1,相当于sizeof(char)
printf("strlen(p) = %d \n",strlen(p)); //5,相当于strlen(srt)

return 0;
}

   

Sizeof(变量)和sizeof(变量类型)的结果是一样的。如上图中的sizeofp)和sizeofchar *)是一样的。其他的变量和其对应的变量类型亦如此。

 

1.9指针与函数传参

1.9.1普通变量作为函数形参

题目1:普通变量的形参和实参

#include <stdio.h >

void func1(int b)
{
printf("b = %d\n",b);
printf("&b = %p\n",&b);
}

int main(void)
{ 
int a = 4;
printf("&a = %p\n",&a);
func1(a);

return 0;
}

 

1)函数传参时,普通变量作为参数时,形参和实参名字可以相同也可以不同,实际上都是用实参来替代相对应的形参的。

2&a&b不同,说明实 参a和形参b不是同一个变量,即在内存中ab是独立的两个内存空间。但ab是有关联的,就是将b的值是a赋值而来的。就是很多书上写的“传值调用”(相当于实参做右值,形参做左值)

 

1.9.2数组作为函数形参

题目1:数组的传址

#include <stdio.h>

void func1(int a[])  //形参是数组形式:传递的是数组的首地址
{
printf("the number of array  = %d\n",sizeof(a));

}

void func2(int *a)  //形参是指针形式:传递的是数组的首地址
{
printf("the number of array  = %d\n",sizeof(a));

}

void func3(int *a, int num)  //传递(1)数组的首地址和(2)数组的大小
{
printf("the number of arry =%d\n",num);

}

int main(void)
{
int b[100] ;
int *a;
func1(b);  //4
func2(a);  //4
func3(a,sizeof(b)/sizeof(int)); //100

return 0;
}

 

(1)func1函数传参,形参是可以用数组形式或指针形式。函数形参是数组时,实际传递是不是整个数组,而是数组的首元素首地址,实际相当于传递的是指针(指针指向数组的首元素首地址),两者的作用是一样的,func1func2的结果是一样的。

[]里的数字可有可无,因为传递的都是是首元素首地址地址,如a[]a[数字]是一模一样的

(2)如果要传递一个数组的大小给函数,则添加一个整数型参数,将sizeof(数组名)/sizeof(数组类型)”或者“sizeof(数组名)/sizeof(一个数组元素)”传递给函数,例如sizeof(b)/sizeof(int)或者sizeof(b)/sizeof(b[0])

 

题目2#define  dpchar  char*typedef  char *  tpchar的区别

#include <stdio.h>
#define	dpchar	char* 
typedef char* 	tpchar;

int main(void)
{
dpchar p1,p2; //相当于char * p1,p2; 其中p1是char*类型;p2是char类型
tpchar p3,p4; //tpchar是我们用typedef定义的char*类型,故p3和p4都是char*类型
printf("p1 = %d\n",sizeof(p1)); //4
printf("p2 = %d\n",sizeof(p2)); //1
printf("p3 = %d\n",sizeof(p3));//4
printf("p4 = %d\n",sizeof(p4));//4

return 0;
}

 

结论:define是在预编译阶段将替换其指定的内容,而typedef是定义一种新的数据类型。

1.9.3结构体变量做函数形参

#include <stdio.h>

struct person
{
char age;
int  num;
};

void func(struct person student)
{
printf("student.num=%d\n",student.num);
printf("&student=%p\n",&student);
}

int main(void)
{
struct person student1 ;
student1.age = 20;
student1.num = 30;
printf("sizeof(student) = %d\n",sizeof(student1));
printf("&student1=%p\n",&student1);
func(student1);

return 0;
}

 

结论:结构体的形参student1和实参student的地址不一样,说明两者是在不同的内存空间。结构体变量作为函数形参的时候,实际上和普通变量传参时表现是一样的,即将实参赋值给形参。

问题:如果结构体很大,所以如果直接用结构体变量进行传参,那么函数调用效率就会很低,则怎么解决?

思路:不要传变量,改为传变量的指针(地址)

 #include <stdio.h>

struct person
{
char age;
int  num;
};

void func(struct person *student)  //形参是结构体指针
{
printf("student.num=%d\n",student->num); //(1)结构体指针->成员(2)结构体变量.成员
}

int main(void)
{
struct person student1 ;
student1.age = 20;
student1.num = 30;
func(&student1); //传递结构体的地址 

return 0;
}

 

1.9.4传值调用与传址调用

#include <stdio.h>

void swap1(int a, int b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}

void swap2(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}

int main(void)
{
int a=3,b=5;
printf("a = %d and b = %d\n",a,b);  // a=3 b=5
swap1(a,b);
printf("a = %d and b = %d\n",a,b);  // a=3 b=5
swap2(&a,&b);
printf("a = %d and b = %d\n",a,b);  // a=5 b=3

return 0;
}

 

(1)传值调用:传递给swap1的是ab的值,即将ab的值赋给形参。当函数swap1对形参进行操作时,不会改变实参的值。

(2)传址调用:传递给swap2的是ab的地址,故通过函数swap2通过访问ab地址来改变其值。

(3)结论:这个世界上根本没有传值和传址这两种方式,C语言本身函数调用时一直是传值的,只不过传的值可以是变量名,也可以是变量的指针。

1.10输入型参数与输出型参数

1.10.1函数为什么需要形参与返回值

(1)函数名是一个符号,表示整个函数代码段的首地址,实质是一个指针常量,所以在程序中使用到函数名时都是当地址用的,用来调用这个函数的。

(2)函数体是函数的关键,由一对{}括起来,包含很多句代码,函数体就是函数实际做的工作。

(3)形参列表和返回值形参是函数的输入部分,返回值是函数的输出部分。对函数最好的理解就是把函数看成是一个加工机器(程序其实就是数据加工器),形参列表就是这个机器的原材料输入端;而返回值就是机器的成品输出端。

(4)其实如果没有形参列表和返回值,函数也能对数据进行加工,用全局变量即可。用全局变量来传参和用函数参数列表返回值来传参各有特点,在实践中都有使用。总的来说,函数参数传参用的比较多,因为这样可以实现模块化编程,而C语言中也是尽量减少使用全局变量。

(5)全局变量传参最大的好处就是省略了函数传参的开销,所以效率要高一些;但是实战中用的最多的还是传参,如果参数很多传参开销非常大,通常的做法是把很多参数打包成一个结  构体,然后传结构体变量指针进去

1.10.2函数传参中使用const指针

(1)const一般用在函数参数列表中,用法是const int *p;(意义是指针变量p本身可变的,而p所指向的变量是不可变的)。

(2)const用来修饰指针做函数传参,作用就在于声明在函数内部不会改变这个指针所指向的内容,所以给该函数传一个不可改变的指针(char *p = "linux";这种)不会触发错误;而一个未声明为const的指针的函数,你给他传一个不可更改的指针的时候就要小心了。

1.10.3函数需要向外部返回多个值时怎么办?

#include <stdio.h>

//返回值用于输出对与错

// a是输入型参数;*b是输出型参数

int func(int a, int *b)  
{
int tmp;
tmp = a*5;
if(tmp>100)
{
	return -1;
}
else
{
	*b = tmp;
	return 0; 
}
}

int main(void)
{
int a,b,ret;
a=5;
ret = func(a,&b);
if(-1 == ret)
{
	printf("error\n");
}
else
{
	printf("result = %d\n",b);
}
return 0;
}

 

(1)一般来说,函数的输入部分就是函数参数,输出部分就是返回值。问题是函数的参数可以有很多个,而返回值只能有1个。这就造成我们无法让一个函数返回多个值。

(2)现实编程中,一个函数需要返回多个值是非常普遍的,因此完全依赖于返回值是不靠谱的,通常的做法是用参数来做返回(在典型的linux风格函数中,返回值是不用来返回结果的,而是用来返回0或者负数用来表示程序执行结果是对还是错,是成功还是失败)。

(3)普遍做法,编程中函数的输入和输出都是靠函数参数的,返回值只是用来表示函数执行的结果是对(成功)还是错(失败)。如果这个参数是用来做输入的,就叫输入型参数;如果这个参数的目的是用来做输出的,就叫输出型参数

(4)输出型参数就是用来让函数内部把数据输出到函数外部的。

总结:看到一个函数的原型后,怎么样一眼看出来哪个参数做输入哪个做输出?函数传参如果传的是普通变量(不是指针)那肯定是输入型参数;如果传指针就有2种可能性了,为了区别,经常的做法是:如果这个参数是做输入的(通常做输入的在函数内部只需要读取这个参数而不会需要更改它)就在指针前面加const来修饰;如果函数形参是指针变量并且还没加const,那么就表示这个参数是用来做输出型参数的。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值