分别在不同地方增加变量,用size 命令查看可执行文件的text, data, bss段的大小变化情况
在所有函数外定义变量(变量占用位置+占用长度) 在main函数中定义变量(变量:占用位置) 在其他局部函数中定义变量(变量:占用位置) 0: 1:int i;bss int i;no effect(stack) int i;no effect(stack) 2:int i = 1;data+sizeof(int) int i = 1;no effect(stack ) int i = 1;no effect(stack) 3:static int i;bss+sizeof(int) static int i:bss static int i:bss 4:static int i=2;data+sizeof(int) static int i=4;data static int i=4;data 5:int *arr;bss+sizeof(int*) int *arr;no effect(stack) int *arr;no effect(stack) 6:int *arr=&i;data int *arr=&i2;no effect(stack) int *arr=&i2;no effect(stack) 7:char buf[4];bss+数组长度 char buf[4];no effect(stack ) char buf[4];no effect(stack ) 8:char buf[4]="1234";data+数组长度 char buf[4]="1234";no effect(stack) char buf[4]="1234";no effect(stack)
A static variable declared inside a function is placed in the data or bss portion of memory and can retain it’s value between calls to the same function. In this way it is similar to a global variable, except it is only used in one function. The restriction on the scope of static variables is a compiler implemented restriction; being that, an assembly language program could easily access any data stored in either the data or bss sections of memory.
A static variable declared as a global variable is also called a static external variable. Here the keyword static produces the opposite results as the extern keyword. The variable is global to the file where it is declared, but may not be referenced in any other files. Thus the keyword static can produce a form of data hiding.
If the compiler knows that there is only one pointer to a memory block, it can produce better optimized code. For instance:
void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val) { *ptrA += *val; *ptrB += *val; }
In the above code, the pointers ptrA, ptrB, and val might refer to the same memory location, so the compiler may generate less optimal code:
load R1 ← *val ; Load the value of val pointer load R2 ← *ptrA ; Load the value of ptrA pointer add R2 += R1 ; Perform Addition set R2 → *ptrA ; Update the value of ptrA pointer ; Similarly for ptrB, note that val is loaded twice, because ; ptrA may be equal to val (i.e., point to the same location). load R1 ← *val load R2 ← *ptrB add R2 += R1 set R2 → *ptrB
However, if the restrict keyword is used and the above function is declared as
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);
then the compiler is allowed to assume that ptrA, ptrB, and val point to different locations and updating one pointer will not affect the other pointers. The programmer, not the compiler, is responsible for ensuring that the pointers do not point to identical locations.
Now the compiler can generate better code as follows:
load R1 ← *val load R2 ← *ptrA add R2 += R1 set R2 → *ptrA ; Note that val is not reloaded, ; because the compiler knows it is unchanged load R2 ← *ptrB add R2 += R1 set R2 → *ptrB
Note that the above assembly code is shorter because val is loaded once.
section 3 可执行文件产生过程
![]()
![]()
![]()
制作动态库(共享库)
![]()
![]()
![]()
![]()
ldconfig是一个动态链接库管理命令,为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig. ldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为 /etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表. ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令.
fscanf(f, "%[^|]| %10f %10f\n", str, &x, &y)这种IO格式用法的来源reentrant, async-signal safe, thread-safe
apue
async-signal safe
apue edition3 section 10.6:
The Single UNIX Specification specifies the functions that are guaranteed to be safe to call from within a signal handler. These functions are reentrant and are called async-signal safe by the Single UNIX Specification. Besides being reentrant, they block any signals during operation if delivery of a signal might cause inconsistencies.Most of the functions that are not included in Figure 10.4 are missing because (a) they are known to use static data structures, (b) they call malloc or free, or (c) they are part of the standard I/O library. Most implementations of the standard I/O library use global data structures in a nonreentrant way.
there is only one errno variable per thread, Therefore, as a general rule, when calling the functions listed in Figure 10.4 from a signal handler, we should save and restore errno. (Be aware that a commonly caught signal is SIGCHLD, and its signal handler usually calls one of the wait functions. All the wait functions can change errno.)
thread-safe
apue section 12.5
If a function can be safely called by multiple threads at the same time, we say that the function is thread-safe. If a function is reentrant with respect to multiple threads, we say that it is thread-safe. This doesn’t tell us, however, whether the function is reentrant with respect to signal handlers. We say that a function that is safe to be reentered from an asynchronous signal handler is async-signal safe.知乎
https://www.zhihu.com/question/21526405
很多线程安全的函数都是不可重入的,例如 malloc。 可重入的函数一般也是线程安全的,虽然据说有反例,但我没见过。 Posix中大多数函数都是线程安全的,但只有少数是 async-signal-safe。
C/C++: sizeof, padding, alignement
对齐原则: ## struct各成员的偏移要是本成员的整数倍 ## 整个struct的sizeof()要是最大成员的整数倍(32位系统,gcc,没有开启-malign-double的情况下,最多对齐到4的整数倍 ## 整个struct的起始地址要是本struct最大成员的整数倍 ) 总体来说, 32bit程序和64bit程序长度不一样的有: 指针型, long double, long int(long 和long int 是同一种类型) wikipedia上说的比较详细:https://en.wikipedia.org/wiki/Data_structure_alignment 本地保存wiki副本
| type | 32bit | 64 bit | ||
|---|---|---|---|---|
| sizeof | align | sizeof | align | |
| long | 4 | 4 | 8 | 8 |
| long long | 8 | 4 | 8 | 8 |
| double | 8 |
4-gcc(-mnoalign-double默认,与编译时的编译参数有关) 8-gcc(-malign-double,与编译时的编译参数有关) 8-vc++ | 8 | 8 |
| long double | 12-gcc 8-vc++ | 4-gcc 8-vc++ |
16-gcc 8-vc++ |
16-gcc 8-vc++ |
| pointer | 4 | 4 | 8 | 8 |
验证程序 //64位os, gcc compiler sizeof A=16 sizeof B=24 sizeof(int=)4 sizeof(short)=2 sizeof(long )=8 sizeof(long int)=8 sizeof(long long)=8 sizeof(long double)=16 sizeof(float)=4 sizeof(double)=8 offset A.a=0 offset A.b=4 offset A.c=8 offset A.d=12 ------------------------------------------------------------ offset B.a=0 offset B.b=8 offset B.c=12 offset B.d=16 ------------------------------------------------------------ sizeof(mystructure)=24 ------------------------------------------------------------ offset myStr.a=0 offset myStr.b=8 offset myStr.c=16 // 32位os, gcc compiler, default compiler option: sizeof A=16 sizeof B=20 sizeof(int=)4 sizeof(short)=2 sizeof(long )=4 sizeof(long int)=4 sizeof(long long)=8 sizeof(long double)=12 sizeof(float)=4 sizeof(double)=8 offset A.a=0 offset A.b=4 offset A.c=8 offset A.d=12 ------------------------------------------------------------ offset B.a=0 offset B.b=8 offset B.c=12 offset B.d=16 ------------------------------------------------------------ sizeof(mystructure)=16 ------------------------------------------------------------ offset myStr.a=0 offset myStr.b=4 offset myStr.c=12 // 32位os, gcc compiler, with -malign-double: sizeof A=16 sizeof B=24 sizeof(int=)4 sizeof(short)=2 sizeof(long )=4 sizeof(long int)=4 sizeof(long long)=8 sizeof(long double)=12 sizeof(float)=4 sizeof(double)=8 offset A.a=0 offset A.b=4 offset A.c=8 offset A.d=12 ------------------------------------------------------------ offset B.a=0 offset B.b=8 offset B.c=12 offset B.d=16 ------------------------------------------------------------ sizeof(mystructure)=24 ------------------------------------------------------------ offset myStr.a=0 offset myStr.b=8 offset myStr.c=16
https://www.zhihu.com/question/21821215 无符号:无论是左移还是右移,都是逻辑移位,补零 有符号:左移是逻辑,右移是算术:最高位补符号位. 1000 0000 >> 1 = 1100 0000 有符号右移是唯一特殊的 移位代码
其实这里注重的是要扩展的量是有符号量还是无符号量。若要扩展量为有符号量,不管扩展成有符号还是无符号, 都遵循符号扩展;若要扩展量为无符号量,不管扩展成有符号还是无符号,都遵循零扩展。 https://blog.csdn.net/zhangzhi123456789/article/details/49589137
code
code example
float sum_elements(float a[], unsigned length)
{
int i = 0;
float sum = 0;
for(i = 0; i <= length -1; ++i)
sum += a[i];
return sum;
}
如果我告诉你这是一段有错的代码,可能你也不太相信,因为这个函数的一切看起来是这么的自然,因为数据的长度(或个数)肯定是一个非负数,所以把length声明为一个unsigned很合理,
计算的数据个数和返回类型也正确。的确如此,但是这都是在length不为0的情况,试想,当调用函数时,把0作为参数传递给length会发生什么事情?回想一下前面我们所说的知识,因为length是unsigned类型
,所以所有的运算都被隐式地被强制转换为unsigned类型,所以length-1(即0-1 = -1),-1对应的无符号类型的值为UMax,所以for循环将会循环UMax次,数组也会越界,发生错误。
那么如何优化上面的代码呢?其实答案非常简单,你也可以自己想一想,这里就给出答案吧,就是把for循环改为:
| convert | uint | int | less than int |
|---|---|---|---|
| uint | / | uint | uint |
| int | / | / | int |
| less than int | / | / | / |