main函数可以有参数吗?

(这个问题从Java中已知,有的)

#include <stdio.h>
int main(int argc, char *argv[]) //因为c没有类似STL的功能,因此要用argc变量进行个数传参
{
  return 0;
}

发现可以正常编译与运行

#include <stdio.h>
int main(int argc, char *argv[])
{
  printf("argc: %d\n", argc);
  int i = 0;
  for(; i < argc; i++)
  {
    printf("argv[%d]->%s\n", i, argv[i]);
  }
  return 0;
}

argc为参数个数,argv[]是一个char*类型字符串数组

运行结果如下:

$ ./code 
argc: 1
argv[0]->./code

$ ./code a
argc: 2
argv[0]->./code
argv[1]->a

$ ./code a b 3
argc: 4
argv[0]->./code
argv[1]->a
argv[2]->b
argv[3]->3

我们在命令行输入的本质就是字符串

shell程序获取之后,会根据空格拆分出多个字符串,作为参数传入

上面的./code a b 3拆为./codeab3总共4个字符串

看不到这个现象,因为是bash和系统自动完成的


传参的作用

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
  if(argc != 2)
  {
    printf("参数数量错误!使用格式:%s -a|-b\n", argv[0]);
    return 1;
  }
  if(strcmp(argv[1], "-a") == 0)
  {
    printf("func1\n");
  }
  else if(strcmp(argv[1], "-b") == 0)
  {
    printf("func2\n");
  }
  else
  {
    printf("defalut func\n");
  }
  return 0;
}

可参考ls -a等命令

编译后运行

$ ./code 
参数数量错误!使用格式:./code -a|-b
$ ./code -a
func1
$ ./code -b
func2
$ ./code a
defalut func

细节:

  1. 命令行参数至少为1,argc >= 1,argv[0]一定有元素,指向的就是程序名

  2. 选项是以空格分隔的字符串,一个字符/数字,也是字符串

  3. argv[]的本质是一张“表”,有argc的元素,argv[argc]内容一定为NULL(最后一个元素永远为NULL)

因此可以这样写for循环

for(; argv[i]; i++)
{
  printf("argv[%d]->%s\n", i, argv[i]);
}

有一个现象:

$ ./mycommand 
this is a command
$ mycommand
-bash: mycommand: command not found

执行自己的程序时必须使用./调用

  1. ./相对路径,让OS找到用户要执行的bin(可执行程序)就在当前路径下

  2. Linux系统有类似环境变量的东西,PATH,系统会默认在这里查找程序

  3. PATH变量是shell语法自带的

$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/s……k/.local/bin:/home/s……k/bin
    (其中`:`为分隔符)

    PATH的本质就是一个帮助Linux系统找到程序的变量 (windows同理)
  1. 如果想要将自己的程序可以不使用./调用:

  2. 添加到/usr/bin路径,不推荐:会污染环境变量

  3. 将自己的程序的路径添加到PATH

  4. 修改PATH:

$ PATH=/home/s……k/code/2025_10_16/
$ echo $PATH
/home/s……k/code/2025_10_16/
$ ls
-bash: ls: command not found

不能直接覆盖PATH!会导致系统无法正常查找其它指令

(退出账号重新登录即可恢复PATH为初始状态)

正确写法:

$ PATH=$PATH:/home/s……k/code/

这样就能正确添加路径

添加路径后,在系统中全局有效!(类似ls等命令)

  1. Windows系统也有(很熟悉,笔记就不写了)

环境变量:系统级的变量,变量名和变量内容,往往具有全局属性

使用env命令查看所有的环境变量

$ env
XDG_SESSION_ID=3……1
HOSTNAME=…… # 主机名
TERM=xterm # 终端类型
SHELL=/bin/bash # 当前系统用的shell程序
HISTSIZE=1000 # 记录最近历史命令的数量上限,使用 history 命令查看
SSH_CLIENT=0.0.0.0 3……9 22 # 当前云服务器登录用户的ip地址
SSH_TTY=/dev/pts/2 # 当前用户使用的终端文件
USER=s……k # 用户名 whoami
LD_LIBRARY_PATH=:/home/s……k/.VimForCpp/……
LS_COLORS=……
MAIL=……
PATH=/……bin
PWD=/home/s……k/code/ # 当前路径 pwd
LANG=en_US.UTF-8 # 编码格式
HISTCONTROL=ignoredups
SHLVL=1
HOME=/home/s……k # 家目录
LOGNAME=s……k # 登录用户名
CVS_RSH=ssh
SSH_CONNECTION=0.0.0.0 3……9 0.0.0.0 22
LESSOPEN=||/usr/bin/l…….sh %s
XDG_RUNTIME_DIR=/run/user/1004
_=/usr/bin/env
OLDPWD=/home/s……k/ # 上一次所在的路径,使用 cd - 命令

了解PTAH,HOME,SHELL是什么就行


环境变量与C/C++代码之间的联系

#include <stdio.h>
int main(int argc, char* argv[], char* env[])//env就是环境变量
{
  int i = 0;
  for(; env[i]; ++i)
  {
    printf("env[%d]: %s\n", i, env[i]);
  }
  printf("this is a command\n");
  return 0;
}
$ ./mycommand 
env[0]: XDG_SESSION_ID=3……1
# ……
env[23]: OLDPWD=/home/s……k
this is a command

env[]就是系统的环境变量表


语言中有一个char** environ 一直指向环境变量表

这是获取环境变量的第二种方法

#include <stdio.h>
#include <unistd.h>
int main()
{
  extern char** environ;
  int i = 0;
  for(; environ[i]; ++i)
  {
    printf("environ[%d]->%s\n", i, environ[i]);
  }
  return 0;
}

上面的两种方法都不常用!

getenv可以直接根据变量名获取变量值,否则为NULL

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
  char* whoami = getenv("USER");
  if(whoami == NULL)
  {
    printf("no USER!\n");
    return 0;
  }
  else if(strcmp(whoami, "root") == 0)
  {
    printf("can NOT use root!\n");
  }
  else
  {
    printf("you are %s\n", whoami);
  }
  return 0;
}

这样可以写出一个root用户无法使用的程序


环境变量表存储在bash之中,而bash在内存中,因此重新打开云服务器终端即可恢复环境变量表为初始状态。

因此修改环境变量是内存级的

因为所有用户执行的程序与命令,都是bash的子进程,而子进程是可以访问父进程的数据,因此子进程可以访问到bash修改后的全局变量

#include <stdio.h>
#include <unistd.h>
char* argv[100];
char* env[100];
int main()
{
  argv[0] = (char*)"ls";
  argv[1] = (char*)"-a";
  env[0] = (char*)"PATH=AAAA";
  pid_t id = fork();
  if(id == 0)
  {
    int i = 0;
    for(; argv[i]; ++i)
      printf("%s\n", argv[i]);
    for(i = 0; env[i]; ++i)
      printf("%s\n", env[i]);
  }
  else
    sleep(10000);
}

可以正常打印,说明子进程可以获取父进程的环境变量

我们平时执行的程序,都是bash的子进程,因此这些程序的环境变量都是bash提供

总结:环境变量由父进程提供

因此所有进程都会识别到bash的环境变量


bash的环境变量从何而来?

从Linux系统的配置文件中获取

平时手动在命令行修改的环境变量只改了bash,不会修改配置文件,因此重启bash即可重新获取系统配置文件的环境变量表

从哪个配置文件中获取?

$ cd ~
$ ls -al # 要在家目录中查看
total 100
-rw-r--r--   1   193 Oct 31  2018 .bash_profile
-rw-r--r--   1   355 Nov 27 21:01 .bashrc
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions
alias vim='/home/s……k/.VimForCpp/nvim'
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/.VimForCpp/……
# /etc/bashrc

# System wide functions and aliases
# Environment stuff goes in /etc/profile

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

# are we an interactive shell?
if [ "$PS1" ]; then
 ……
# vim:……

在启动bash时,先调用.bash_profile,然后调用.bashrc,然后调用/etc/bashrc

修改文件:

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH

echo "This is a change in .bash_profile!"

重新启动bash:

Welcome to …… Cloud Elastic Compute Service !

This is a change in .bash_profile!

因为每个用户都有自己的家目录,因此只会修改当前用户的bash


本地变量

$ TEST_ENV=12345
$ env | grep TEST_ENV
$ echo $TEST_ENV
12345

这种在shell中定义的是本地变量,不会保存在环境变量表中

使用export导出至环境变量表

$ export TEST_ENV
$ env
# ...
TEST_ENV=12345
# ...

此时 TEST_ENV 也可以被程序获取

使用unset删除环境变量

$ unset TEST_ENV
$ env
# 没有 TEST_ENV

以上操作都是内存级

本地变量:

  • 本地变量无法被子进程继承,不具备全局性,只在bash内部可以访问

  • 使用export写入变量表,变为全局变量

  • 本地变量有自己的语法

$ cnt=0; while [ $cnt -le 10 ]; do echo $cnt; let cnt++; done
0
1
2
3
4
5
6
7
8
9
10

使用env查看环境变量,使用set查看环境变量和本地变量


如果需要将环境变量写为永久,就写到 ~/.bash_profile


$ local_value=100
$ env | grep local_value
$ echo $local_value
100

local_value是本地变量,为什么echo可以获取到该变量?

$ PATH=''
$ echo $PATH

$ ls
-bash: ls: No such file or directory
$ pwd
/home/……/code/
$ top
-bash: top: No such file or directory
$ echo "aa"
aa

明明PATH为空,但是一部分程序能运行,一部分不能运行?

命令与命令之间是不一样的

  • 存在的二进制级别的命令:普通命令

  • 内建命令:在shell内部自己定义,bash内部的一次函数调用,不依赖第三方路径

    相当于编程语言中语言自带的函数(暂时这样理解!)

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐