int main(int argc, char *argv[ ])与 int main(int argc, char **argv)
今天看到Linux中的main函数居然是这样定义的,和之前学习C语言和单片机时写法不一样,仔细研究过才发现,这才是最正统的main函数的写。
单片机开发中,main函数不需要传参,所以才写成了 void main (void),Linux下可以不是这样了
举个例子
先不用管这个Makefile的具体内容
led.bin: start.o
arm-linux-ld -Ttext 0x0 -o led.elf $^
arm-linux-objcopy -O binary led.elf led.bin
arm-linux-objdump -D led.elf > led_elf.dis
gcc mkv210_image.c -o mkx210
./mkx210 led.bin 210.bin
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
注意这一句 :
./mkx210 led.bin 210.bin
mkx210是Linux下的可执行文件,需要传入led.bin 和210.bin做参数,由gcc mkv210_image.c -o mkx210可知, int main(int argc, char *argv[ ])应该在mkv210_image.c中
argc
是命令行总的参数个数,argv[ ]取决于argc的大小
char *argv[]是一个字符数组,其大小是int argc,命令行总共有三个参数,mkx210、 led.bin 、210.bin,所以:
argc = 3
char *argv[ ]
这是一个char*类型的指针数组,里面存放了一个个的字符串的指针,进一步讲这是“传址调用”。
argv数组中的第一个单元指向的字符串总是可执行程序的名字,以后的单元指向的字符串依次是程序调用时的参数。
argv[0] = “mkx210”
argv[1] = “led.bin”
argv[2] = “210.bin”
传参前,编译器会把这些可执行程序的名字和需要调用的程序的名字做成一个字符串数组,main函数只要有对应的接口就可以访问到它们了,就像这样:
char *argv[ ] = {“mkx210” ,“led.bin” ,“210.bin”};
或者也可以把这个字符串数组的首地址赋值给一个二重指针
char **argv = {“mkx210” ,“led.bin” ,“210.bin”};
至于二重指针与一重指针的区别,我也是刚刚接触,我是这样理解的
存放着普通变量地址的变量是一重指针,存放一重指针变量本身的地址(不是一重指针变量指向的地址) 的变量是二重指针
首先二重指针argv是 char** 类型的,所谓 char** 类型就是指针指向的变量是char* 类型的,而 char* 类型又表示指针指向的变量是char类型的,而C语言中操作字符串其实就是操作字符串的首地址
那我们对照着来看一下
形参如果这么写:char *argv[ ]
那么字符串数组传参后
其首元素 argv[0] 存放着字符 ‘m’ 的地址即字符串 “mkx210” 的起始地址
形参如果这样写:char **argv
那么字符串数组传参后
argv这个二重指针存放着字符串数组首元素的地址,
*argv
即解引用argv,得到字符串数组首元素的内容,即字符串 “mkx210” 的起始地址
(注意:字符串数组首地址并不等于里面存放的第一个字符串的首地址,里面的字符串是程序编译的时候就已经分配在静态存储区的常量,它的地址在静态存取区,具体详见这篇博客中的内存分配方式部分戳我,还有一些C语言容易混淆的概念,见深入 char * ,char ** ,char a[ ] ,char *a[] 内核
)
如何调用
无论是哪种写法,在mkv210_image.c中都可以按照如下方式调用需要的程序
毕竟argv为指针或数组名(与指针等效),argv[ i ]中存放的依旧是指针变量,最终都可找到存放在静态存储区的字符串首地址
以上内容参考了上面两位博主的博客以及友善之臂和朱有鹏老师的文档
特此感谢
另外,本人初学嵌入式Linux,学习之路很是漫长,好记性不如烂博客,如有错误的地方,欢迎评论区指出交流,谢谢支持