0%

whatis 你是干什么的

其实整个命令已出现,你的脑海里面应该浮现的是:

What is your name?

如题所述,这个命令用于查询一个命令到底执行了什么功能,并将查询的结果输出出来,相当于man的一个选项-f

whatis的官方定义为:

whatis - display manual page descriptions

仅仅提供一个比较简单的命令描述.

使用方法也比较简单,如下:

1
$ whatis [options] name

其中的name可以是Linux命令、系统调用、库函数、系统等等内容

以前面的命令为例,执行如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ whatis ls cd file cat more less
ls (1) - list directory contents
ls (1p) - list directory contents
cd (1) - bash built-in commands, see bash(1)
cd (1p) - change the working directory
cd (n) - Change working directory
file (1) - determine file type
file (1p) - determine file type
file (n) - Manipulate file names and attributes
cat (1) - concatenate files and print on the standard output
cat (1p) - concatenate and print files
more (1) - file perusal filter for crt viewing
more (1p) - display files on a page-by-page basis
less (1) - opposite of more
less (3pm) - perl pragma to request less of something

可以看到whatis是支持同时查询多个命令的

拓展

whatis可以通过-w-r以及-C等选项来设定通配符、正则表达式以及配置文件等等,不过最简单的还是简单查看一个命令的简单描述,其他的可以交给man来处理。

聊聊main函数

main字面的意思就是主要的意思,在程序里面也是,并且更重要了,是主函数,何为主函数,就是程序运行会执行的第一个程序,意即程序的入口,如果说眼睛是心灵的窗户,那么main就是程序的大门。

在C99标准之前,main函数可以没有返回类型或者使用void,如下所示:

1
2
3
4
5
/*beginner/main/main1.c*/

main()
{
}

或者

1
2
3
4
5
6
/*beginner/main/main2.c*/

void main()
{
}

这两个可以称为最短小精悍的程序了,不会出错,因为没有语句。

如果使用C99标准编译,就可以看到如下的警告信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
main1.c:3:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
main()
^
1 warning generated.
gcc -o main2 main2.c
main2.c:3:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main()
^
main2.c:3:1: note: change return type to 'int'
void main()
^~~~
int
1 warning generated.

警告的含义就是我们的mian函数是有血有肉的,是有正儿八经的返回值的,这个返回值的类型就是int,所以这个程序:

1
2
3
4
5
/*beginner/main/main3.c*/

int main()
{
}

OK,完美编译,没有任何报错信息,不过终归看着少了点什么,是的,如果定义了返回类型,就需要有相应的返回,所以第一个标准的简单C语言函数如下所示:

1
2
3
4
5
6
/*beginner/main/main4.c*/

int main()
{
return 0;
}

main函数的形式

恩,既然写出了main函数的标准形式,这里就需要讨论一个问题,为什么有时候看到int main(),有时候又看到int main(int argc, char *argv[])呢,具体的形式如下所示:

无参数的main函数

1
2
3
4
5
6
7
8
/*beginner/main/main5.c*/

int main()
{
printf("Hello World\n");
return 0;
}

可以看到上面这个例子,只是打印了hello world,并没有其他语句。

而下面的例子,我们看到使用到了argc和argv,所谓的形参,所以在有需要的时候才会出现这种形式,就好像是黄蓉给郭靖的几个锦囊^_^。

有参数的main函数

1
2
3
4
5
6
7
8
9
10
11
12
/*beginner/main/main6.c*/

int main(int argc, char *argv[])
{
printf("The are %d arguments.\n", argc);
int i;
for (i = 0; i < argc; i++)
printf("%s ", argv[i]);
printf("\n");
return 0;
}

编译运行

直接输入make就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#beginner/main/Makefile
ALL : main1 main2 main3 main4 main5 main6

main1: main1.c
gcc -o main1 main1.c

main2: main2.c
gcc -o main2 main2.c

main3: main3.c
gcc -o main3 main3.c

main4: main4.c
gcc -o main4 main4.c

main5: main5.c
gcc -o main5 main5.c

main6: main6.c
gcc -o main6 main6.c

.PHONY : clean

clean:
rm -f main1 main2 main3 main4 main5 main6

MALLOC(3) Linux Programmer’s Manual MALLOC(3)

NAME
malloc, free, calloc, realloc - allocate and free dynamic memory

SYNOPSIS
#include <stdlib.h>

   void *malloc(size_t size);
   void free(void *ptr);
   void *calloc(size_t nmemb, size_t size);
   void *realloc(void *ptr, size_t size);
   void *reallocarray(void *ptr, size_t nmemb, size_t size);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

   reallocarray():
       Since glibc 2.29:
           _DEFAULT_SOURCE
       Glibc 2.28 and earlier:
           _GNU_SOURCE

DESCRIPTION
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique
pointer value that can later be successfully passed to free().

   The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc(), or realloc().  Otherwise, or if free(ptr) has al‐
   ready been called before, undefined behavior occurs.  If ptr is NULL, no operation is performed.

   The  calloc()  function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory.  The memory is set to zero.  If nmemb or size
   is 0, then calloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().  If the multiplication of nmemb and size would  result  in  integer
   overflow, then calloc() returns an error.  By contrast, an integer overflow would not be detected in the following call to malloc(), with the result that an incorrectly sized block of
   memory would be allocated:

       malloc(nmemb * size);

   The realloc() function changes the size of the memory block pointed to by ptr to size bytes.  The contents will be unchanged in the range from the start of the region up to the  mini‐
   mum of the old and new sizes.  If the new size is larger than the old size, the added memory will not be initialized.  If ptr is NULL, then the call is equivalent to malloc(size), for
   all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).  Unless ptr is NULL, it must have been returned by an earlier call to mal‐
   loc(), calloc(), or realloc().  If the area pointed to was moved, a free(ptr) is done.

   The reallocarray() function changes the size of the memory block pointed to by ptr to be large enough for an array of nmemb elements, each of which is size bytes.  It is equivalent to
   the call

           realloc(ptr, nmemb * size);

   However, unlike that realloc() call, reallocarray() fails safely in the case where the multiplication would overflow.  If such an overflow occurs, reallocarray()  returns  NULL,  sets
   errno to ENOMEM, and leaves the original block of memory unchanged.

RETURN VALUE
The malloc() and calloc() functions return a pointer to the allocated memory, which is suitably aligned for any built-in type. On error, these functions return NULL. NULL may also
be returned by a successful call to malloc() with a size of zero, or by a successful call to calloc() with nmemb or size equal to zero.

   The free() function returns no value.

   The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any built-in type, or NULL if the request failed.  The returned  pointer  may  be
   the  same as ptr if the allocation was not moved (e.g., there was room to expand the allocation in-place), or different from ptr if the allocation was moved to a new address.  If size
   was equal to 0, either NULL or a pointer suitable to be passed to free() is returned.  If realloc() fails, the original block is left untouched; it is not freed or moved.

   On success, the reallocarray() function returns a pointer to the newly allocated memory.  On failure, it returns NULL and the original block of memory is left untouched.

ERRORS
calloc(), malloc(), realloc(), and reallocarray() can fail with the following error:

   ENOMEM Out of memory.  Possibly, the application hit the RLIMIT_AS or RLIMIT_DATA limit described in getrlimit(2).

ATTRIBUTES
For an explanation of the terms used in this section, see attributes(7).

   ┌─────────────────────┬───────────────┬─────────┐
   │Interface            │ Attribute     │ Value   │
   ├─────────────────────┼───────────────┼─────────┤
   │malloc(), free(),    │ Thread safety │ MT-Safe │
   │calloc(), realloc()  │               │         │
   └─────────────────────┴───────────────┴─────────┘

CONFORMING TO
malloc(), free(), calloc(), realloc(): POSIX.1-2001, POSIX.1-2008, C89, C99.

   reallocarray() is a nonstandard extension that first appeared in OpenBSD 5.6 and FreeBSD 11.0.

NOTES
By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. In case
it turns out that the system is out of memory, one or more processes will be killed by the OOM killer. For more information, see the description of /proc/sys/vm/overcommit_memory and
/proc/sys/vm/oom_adj in proc(5), and the Linux kernel source file Documentation/vm/overcommit-accounting.rst.

   Normally, malloc() allocates memory from the heap, and adjusts the size of the heap as required, using sbrk(2).  When allocating blocks of memory larger than MMAP_THRESHOLD bytes, the
   glibc  malloc()  implementation  allocates the memory as a private anonymous mapping using mmap(2).  MMAP_THRESHOLD is 128 kB by default, but is adjustable using mallopt(3).  Prior to
   Linux 4.7 allocations performed using mmap(2) were unaffected by the RLIMIT_DATA resource limit; since Linux 4.7, this limit is also enforced for allocations performed using mmap(2).

   To avoid corruption in multithreaded applications, mutexes are used internally to protect the memory-management data structures employed by these functions.  In a multithreaded appli‐
   cation  in  which  threads  simultaneously  allocate and free memory, there could be contention for these mutexes.  To scalably handle memory allocation in multithreaded applications,
   glibc creates additional memory allocation arenas if mutex contention is detected.  Each arena is a large region of memory that is internally allocated by the system (using brk(2)  or
   mmap(2)), and managed with its own mutexes.

   SUSv2 requires malloc(), calloc(), and realloc() to set errno to ENOMEM upon failure.  Glibc assumes that this is done (and the glibc versions of these routines do this); if you use a
   private malloc implementation that does not set errno, then certain library routines may fail without having a reason in errno.

   Crashes in malloc(), calloc(), realloc(), or free() are almost always related to heap corruption, such as overflowing an allocated chunk or freeing the same pointer twice.

   The malloc() implementation is tunable via environment variables; see mallopt(3) for details.

SEE ALSO
valgrind(1), brk(2), mmap(2), alloca(3), malloc_get_state(3), malloc_info(3), malloc_trim(3), malloc_usable_size(3), mallopt(3), mcheck(3), mtrace(3), posix_memalign(3)

   For details of the GNU C library implementation, see ⟨https://sourceware.org/glibc/wiki/MallocInternals⟩.

COLOPHON
This page is part of release 5.05 of the Linux man-pages project. A description of the project, information about reporting bugs, and the latest version of this page, can be found at
https://www.kernel.org/doc/man-pages/.

GNU 2020-02-09 MALLOC(3)

聊聊main函数

main字面的意思就是主要的意思,在程序里面也是,并且更重要了,是主函数,何为主函数,就是程序运行会执行的第一个程序,意即程序的入口,如果说眼睛是心灵的窗户,那么main就是程序的大门。

在C99标准之前,main函数可以没有返回类型或者使用void,如下所示:

1
2
3
4
5
/*beginner/main/main1.c*/

main()
{
}

或者

1
2
3
4
5
6
/*beginner/main/main2.c*/

void main()
{
}

这两个可以称为最短小精悍的程序了,不会出错,因为没有语句。

如果使用C99标准编译,就可以看到如下的警告信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
main1.c:3:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
main()
^
1 warning generated.
gcc -o main2 main2.c
main2.c:3:1: warning: return type of 'main' is not 'int' [-Wmain-return-type]
void main()
^
main2.c:3:1: note: change return type to 'int'
void main()
^~~~
int
1 warning generated.

警告的含义就是我们的mian函数是有血有肉的,是有正儿八经的返回值的,这个返回值的类型就是int,所以这个程序:

1
2
3
4
5
/*beginner/main/main3.c*/

int main()
{
}

OK,完美编译,没有任何报错信息,不过终归看着少了点什么,是的,如果定义了返回类型,就需要有相应的返回,所以第一个标准的简单C语言函数如下所示:

1
2
3
4
5
6
/*beginner/main/main4.c*/

int main()
{
return 0;
}

main函数的形式

恩,既然写出了main函数的标准形式,这里就需要讨论一个问题,为什么有时候看到int main(),有时候又看到int main(int argc, char *argv[])呢,具体的形式如下所示:

无参数的main函数

1
2
3
4
5
6
7
8
/*beginner/main/main5.c*/

int main()
{
printf("Hello World\n");
return 0;
}

可以看到上面这个例子,只是打印了hello world,并没有其他语句。

而下面的例子,我们看到使用到了argc和argv,所谓的形参,所以在有需要的时候才会出现这种形式,就好像是黄蓉给郭靖的几个锦囊^_^。

有参数的main函数

1
2
3
4
5
6
7
8
9
10
11
12
/*beginner/main/main6.c*/

int main(int argc, char *argv[])
{
printf("The are %d arguments.\n", argc);
int i;
for (i = 0; i < argc; i++)
printf("%s ", argv[i]);
printf("\n");
return 0;
}

编译运行

直接输入make就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#beginner/main/Makefile
ALL : main1 main2 main3 main4 main5 main6

main1: main1.c
gcc -o main1 main1.c

main2: main2.c
gcc -o main2 main2.c

main3: main3.c
gcc -o main3 main3.c

main4: main4.c
gcc -o main4 main4.c

main5: main5.c
gcc -o main5 main5.c

main6: main6.c
gcc -o main6 main6.c

.PHONY : clean

clean:
rm -f main1 main2 main3 main4 main5 main6


一旦当我懂得了珍惜的时候 本该珍惜的东西早已悄悄地从这个世界溜走了 - 季羡林 《赋得永久的悔》


能够百分之六十为他人着想 百分之四十为自己着想 他就是一个及格的好人 - 季羡林 《季羡林读书与做人》


逢人不说人间事 便是人间无事人 - 杜荀鹤《赠质上人》


黑夜给了我黑色的眼睛 我却用它来寻找光明 - 顾城《一代人》



不是成功的路太远
而是能坚持的人不多


这个世界上总会有人陪你走一段
只要你坚持下去


当你对这个世界敞开怀抱的时候

这个世界才会对你敞开怀抱

当你拒绝这个世界

你也必然会被这个世界所拒绝。


最可怕的敌人

就是没有坚强的信念

  • 罗曼·罗兰

意志坚强的人

能把世界放在手中

像泥块一样任意揉捏

  • 歌德


我曾经跨过山和大海 也穿过人山人海 我曾经拥有着的一切 转眼都飘散如烟 - 朴树《平凡之路》


C语言 argc和argv

argcargv是最熟悉的陌生人。

为什么说他们熟悉,是因为如果你用的IDE比较多,大部分的main函数的样子是这样子的int main(int argc, char *argv[])

说它们陌生是因为,你可能写了半年的程序也没有实际用过这两个参数,不明所以。

好的,先来说说它们的叫法,或许也不对:

  • argc:argument counter,读作arg - C,是一个整型
  • argv:argument vector,读作 arg-V,是一个二维字符数组

具体什么意思呢,还记不记得前面说的scanf,可以采集键盘输入,恩,运行程序,然后等待我们输入,这两个参数的含义是在运行程序的时候就直接输入,比如,有两个程序bigger和smaller,我想后面直接跟两个参数来输入较大值 或者较小值,用法如下:

1
2
$ ./bigger 5 4
$ ./smaller 15 23

这个时候就需要argc和argv了。

比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
/*beginner/argcargv/argcargv1.c*/

#include <stdio.h>

int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);
return 0;
}

编译运行:

1
2
3
4
5
6
7
8
9
10
11
12
$ ./argcargv1 
argc = 1
$ ./argcargv1 1
argc = 2
$ ./argcargv1 1 2
argc = 3
$ ./argcargv1 1 2 3
argc = 4
$ ./argcargv1 1 2 3 4
argc = 5
$ ./argcargv1 1 2 3 4 1234
argc = 6

哇,amazingargc的值根据你的输入参数在自动修改,这就是argc的意思,就是命令行的个数统计。

下面看看argv的用法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/*beginner/argcargv/argcargv2.c*/

#include <stdio.h>

int main(int argc, char *argv[])
{
printf("argv are :\n");
int i;
for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}

这里有个需要留意的地方,就是我们在使用argv的时候需要配合argc,因为这就涉及到数组溢出的issue了。

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ./argcargv2 1
argv are :
argv[0] = ./argcargv2
argv[1] = 1
$ ./argcargv2 1 2
argv are :
argv[0] = ./argcargv2
argv[1] = 1
argv[2] = 2
$ ./argcargv2 1 2 34
argv are :
argv[0] = ./argcargv2
argv[1] = 1
argv[2] = 2
argv[3] = 34

综上,我们就可以写个bigger的demo程序了,先自己想想如何写。


代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*beginner/argcargv/argcargv3.c*/

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{

if (argc != 3)
{
printf("Usage :\n");
printf(" ./argcargv3 num1 num2\n");
exit(0);
}

if (atoi(argv[1]) > atoi(argv[2]))
printf("Bigger is %d\n", atoi(argv[1]));
else
printf("Bigger is %d\n", atoi(argv[2]));
return 0;
}

测试如下:

1
2
3
4
5
6
7
8
9
$ ./argcargv3
Usage :
./argcargv3 num1 num2

$ ./argcargv3 3 6
Bigger is 6

$ ./argcargv3 9 6
Bigger is 9

哇哦,又来了一点新东西,首先头文件加了stdlib,这个主要是因为使用了函数atoi,这个函数的含义就是把字符转换为数字,因为命令行参数的类型是char *的,所以在进行比较的时候要转换为整数。

编译运行

直接输入make就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#beginner/argcargv/Makefile
ALL : argcargv1 argcargv2 argcargv3

argcargv1: argcargv1.c
gcc -o argcargv1 argcargv1.c

argcargv2: argcargv2.c
gcc -o argcargv2 argcargv2.c

argcargv3: argcargv3.c
gcc -o argcargv3 argcargv3.c

.PHONY : clean

clean:
rm -f argcargv1 argcargv2 argcargv3

C语言的原码、反码和补码

经典著作《C和指针》说过,对于C语言而言,只有四种类型:

  1. 整形
  2. 浮点型
  3. 指针型
  4. 聚合型(比如数组、结构体等)

有人就说了,明显的有char类型吗,不过你可以看看char的范围,就能理解这个char是一个微型的正数,范围在-128~127或者0-255之间,为什么会是两个范围呢,仔细想想signedunsigned的区别。

建议阅读这个经典著作,对指针的理解会上升一个层次

其中整数在计算机内部基本使用补码来表示,这里就说下原码、反码和补码。

先抛出他们的定义:

  • 原码:原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值

  • 反码:正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.

  • 补码:正数的补码就是其本身,负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1(也可以认为是在反码的基础上+1)

举个例子说明一下:

类别 正数 负数 备注(十进制)
原码 0b00011011 0b10011011 27/-27
反码 0b00011011 0b11100100 27/-100
补码 0b00011011 0b11100101 27/-101
对于计算机而言,能理解的只有二进制,也就是电路里面的高低或者叫开关。

C语言的符号常量

​ 前面已经看完了变量,正常情况下就要来聊聊常量了,从字面意义上看,变量与常量的概念比较容易理解:

  • 变量:就是能改变的量
  • 常量:就是不能改变的量

对于变量而言用的最多的也是见得做多的估计就是int i;这个语句的,而常量在什么时候用呢?

比如银行的利率,蔬菜的价格,电路的增益,线缆的损耗等等,可能会更改的值,更改不是原因,主要是在使用的时候一定要是多处使用到了,这样才能发挥它的作用。

先说一下符号变量的一般用法,如下所示:

1
#define 变量名 变量值

来个不用代码片段的,比如白菜的价格是2.5元一斤,张三、李四、王五,每个人来个三四五斤,需要多少钱,代码如下:

1
2
3
float zhangsan_price = 2.5 * 3;
float lisi_prince = 2.5 * 4;
float wangwu_prince = 2.5 * 5;

不错,代码看着简介明了,忽然章丘遇到天灾了,白菜统一涨价到5.5元一斤,OK,代码修改如下所示:

1
2
3
float zhangsan_price = 5.5 * 3;
float lisi_prince = 5.5 * 4;
float wangwu_prince = 5.5 * 5;

真是中国好邻居呀,干嘛都一起,这样也好操作,但是如果要统计一个村300多户都买几斤大白菜,那么需要修改的就是300多处,那样岂不是超级崩溃,OK,这时就需要符号常量了,我们略微修改,增加符号变量:

1
2
3
4
5
#define PRICE 2.5

float zhangsan_price = PRICE * 3;
float lisi_prince = PRICE * 4;
float wangwu_prince = PRICE * 5;

涨价了怎么办呢,只需要需改第一句,把2.5更改为5.5即可,简单快捷粗暴:

1
2
3
4
5
#define PRICE 5.5

float zhangsan_price = PRICE * 3;
float lisi_prince = PRICE * 4;
float wangwu_prince = PRICE * 5;

300多户,丝毫不惧,来个百八十万照旧一次修改,全部收益。

恩,符号变量的效果就是如此了,你是否已经了然于胸。

来个小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*beginner/define/define1.c*/
#include <stdio.h>

#define BONUS 0.1

int main()
{
int salary;

salary = 30;

printf("The salary this year is %.2fk\n", salary * (1 + BONUS));

return 0;
}

每年浮动10%应该是常态,希望各位老板仁慈,对待优秀员工,把整个符号常量设大一点,哈哈,不用太多,0.5就可以了,😁。

相应地Makefile如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
#beginner/define/Makefile

ALL : define1

define1 : define1.c
gcc -o define1 define1.c


.PHONY : clean

clean:
rm -f define1

输入make,然后执行各个程序输出如下:

1
2
$  ./define1
The salary this year is 33.00k