数据结构是编程的核心,还是算法是编程的核心,这一直是一个争论的话题。正因为如此,说明了数据结构与算法在编程领域的核心地位。------程序员
苏格拉没有底与糕点
苏格拉没有底是一个伟大的哲学家,他很喜欢吃一家店的现做的糕点,但是他每天都忙与奔波,所以不能每天都吃到糕点。一天,他突然想到一个好办法。他每天路过糕点店的时候,会把糕点钱和一张写有地址的纸条留给糕点店,糕点店做好蛋糕之后就会把蛋糕放到纸条上的指定地点去,然后苏格拉没有底就可以在这个指定地点找到自己想吃的蛋糕啦,好开心~
好了,输入输出参数讲解完啦。。。
糕点店其实就是一个函数(因为它实现了制作糕点的功能),苏格拉没有底把两类东西给了它,一类是糕点钱,它用作输入目的,因为钱是用来买制作糕点用的材料的;另一类是写有地址的小纸条,它用作输出目的,因为它指定了糕点完成之后糕点的交货地点,哈哈;
最后再来说一下,既然输出参数是个地址,那它一定是个指针啦,而这个指针指向的地址最终会存放函数的运算结果~
通过实际的例子形象的描述清除了问题的解决方案。正如哲学家就餐问题,说明了线程,互斥 一样。但是更深的细节还需要我们去挖掘和体会。
C语言深度学习---输入型参数和输出型参数
1、函数为什么需要形参和返回值
(1)函数名是一个符号表示整个函数代码段的首地址, 实质是一个指针的常量,指针常量指向这个函数用到的函数名都是当地 址用的,用来调用这个函数的。
(2)函数体是函数的关键,由一对{}括起来,包含很多句代码。函数体就是函数实际上做的工作。
(3)形参的列表和返回值 形参是函数的输入部分,返回值是函数的输出部分,对函数最好的理解就是把函数理解成一个加工的 机器。 把数据进行加工,得到输出值。 程序其实就是数据的加工器。 那么我们形参的列表就是这个机器的原材料的输入 端,而返回值就是机器的成品输出端。
(4)其实如果没有形参列表,函数也能对数据进行加工,用全局变量就可以了。
在嵌入式里面,除了你的产品占用的结构体,其它地方最好不要用到全局变量。
因为全局变量是非常的耗费内存的,所以我们要总是使用局部的变量。
两种方法都有自己的优点和缺点:
A: 函数参数传参用的比较多,因为这样可以实现模块化的编程,而C语言也是尽量减少使用全局变量。
B:Linux内核在早期审查的不是十分的严格,所以出现了很多全局变量。使用的函数
C:全局变量的传参是十分快的,参数很多情况下面对它进行打包成一个结构体。省略了函数传参的开销,所以效率要高一些,但是实战中用的最多的还是传参,如果参数很多传参的开销非常大的,通常的做法是把很多参数打包成一个结构体,然后传结构体变量的指针进去。
2、函数传参的使用const指针
(1)const一般是在函数的参数的列表当中的,用法是const int *P意义是指针变量P本身是可变的,而P指向的变量是不可变的。
(2)char *pstr = “Linux”; //存放在代码段的
char pstr[] = "linux" ; //因为数组是存放在数据段的
(3)const用来修饰指针做函数的参数,作用就是在于函数内部不会改变这个指针所指向的内容,所以给该函数传一个不可改变的指针(char *p ="linux")不会触发错误,而是一个未声明为const指针的函数。总之:你看到const参数,他已经默认叫你不要去随便的修改。
(4)函数需要向外部返回多个值的时候该怎么办?
PS:一开始我的想法是那你不会弄一个结构体传出去啊?其实传出去的就是结构体的指针,所以你的输出型参数在多个时候
就是要用指针传递
(1)一般来说,函数的输入部分就是函数的参数,输出部分就是返回值,估计很多C语言初学者也会这么认为的。
(2)问题是如果函数的参数可以有很多个,而返回值只能有一个,这就会让我们无法让一个函数返回多个值。
(3)现实编程中,一个函数需要返回多个值是非常普遍的,通常的做法是用参数来返回。
在典型的Linux返回值是不用来返回结果的,返回值的作用是用来返回0或者一个负数,用来表示程序执行的结果是对还是错的。
(4)普遍的做法,编程中函数的输入和输出都是靠函数的参数的,返回值只是用来表示函数的执行结果是对的还是错。
(5)如果这个函数是用来做输入的,也叫做输入型参数,用来做输出的就叫做输出型参数。输出型参数就是让函数把函数内部把数据输出到函数的外部的。
#include <stdio.h>
Int multip5_3(int a,int *p);
int main(void)
{
Int a,b,ret = -1;
a= 3;
Mutip5_3(a,&b);
}
Int multip5_3( int a , int *p )
{
*p = 5 * a;
Return 0;
}
总结:
(1)看到一个函数的原型的时候,怎么样看出来哪个参数做输入或者输出型的参数。
只能传进去的指针,才能是输出型参数。
(2)函数传参如果传的是普通的变量的话,肯定是输入型的参数。如果传递指针的话那么就有可能是输入也有可能是输出型的参数。
(3)为了区别,经常的做法是,如果这个参数是做输入的,(通常做输入的在函数内部只需要读取这个参数,而不会需要更改 它)就在指针前面加const来修饰。如果函数形参是指针变量并且还没有加const,那么就表示这个参数是用来做输出型数。
(4)别人在调用你写的函数的时候或者你读别人的函数的时候,有很多隐含的意义。
举例子:
1.比如我们C库函数中,char * strcpy(char *dest,const char *src);
Src 是只读的,不需要改写的, dest就是一个输出型的参数的实例。
2.比如以下代码:
void LoRaMacPayloadEncrypt(
const uint8_t *buffer, //指针做输入(指向的内容不可以修改)
uint16_t size, //普通输入
const uint8_t *key, //指针做输入(指向的内容不可以修改)
uint32_t address, //普通输入
uint8_t dir, //普通输入
uint32_t sequenceCounter, //普通输入
uint8_t *encBuffer ) //指针做输出(没有const进行修饰)
{
uint16_t i;
uint8_t bufferIndex = 0;
uint16_t ctr = 1;
memset1( AesContext.ksch, '\0', 240 );
aes_set_key( key, 16, &AesContext );
aBlock[5] = dir;
aBlock[6] = ( address ) & 0xFF;
aBlock[7] = ( address >> 8 ) & 0xFF;
aBlock[8] = ( address >> 16 ) & 0xFF;
aBlock[9] = ( address >> 24 ) & 0xFF;
aBlock[10] = ( sequenceCounter ) & 0xFF;
aBlock[11] = ( sequenceCounter >> 8 ) & 0xFF;
aBlock[12] = ( sequenceCounter >> 16 ) & 0xFF;
aBlock[13] = ( sequenceCounter >> 24 ) & 0xFF;
while( size >= 16 )
{
aBlock[15] = ( ( ctr ) & 0xFF );
ctr++;
aes_encrypt( aBlock, sBlock, &AesContext );
for( i = 0; i < 16; i++ )
{
encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i];
}
size -= 16;
bufferIndex += 16;
}
if( size > 0 )
{
aBlock[15] = ( ( ctr ) & 0xFF );
aes_encrypt( aBlock, sBlock, &AesContext );
for( i = 0; i < size; i++ )
{
encBuffer[bufferIndex + i] = buffer[bufferIndex + i] ^ sBlock[i];
}
}
}