《K&R》04-函数与程序结构

作用域

  • 对于在函数开头声明的自动变量,其作用域是声明该变量的函数。
  • 外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾。
  • 如果在外部变量声明之前使用该变量,或者外部变量的声明和使用不在同一个文件中,则必须在相应的变量声明中使用关键字extern
#include <stdio.h>

int i; 

int main()
{
    // 先于main声明变量i,在main函数中可直接使用
    printf("%d", i;

    return 0;
}
int main()
{
    // 编译报错
    // error: 'i' undeclared (first use in this function
    printf("%d", i);

    return 0;
}

int i;
int main()
{
    // 通过extern声明来访问一个尚未声明的外部变量
    extern int i;
    printf("%d", i); // 100

    return 0;
}

int i = 100;

静态变量(static关键字)

  • static修饰外部变量(或函数),则该变量仅限当前文件中可以访问。其他文文件不能通过extern关键字来访问。
  • static修饰函数内部变量,则无论函数是否被调用,该变量将一直存在,而不像自动变量那样,随着函数的被调用和退出而存在和消失。

寄存器变量

  • register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在机器的寄存器中,这样可以使程序更小,执行速度更快。但编译器可以忽略此选项。
  • register仅适用于函数的形式参数和自动变量。
f (register unsigned m, register long n)
{
    register int i;
    ...
}

初始化

  • 如果没有显式的初始化,外部变量及静态变量将被初始化为0,自动变量及寄存器变量则不的初值则没有定义(即初值为无用的信息)。
  • 对于外部变量及静态变量来说,初始化表达式必须是常量表达式。

数组初始化

  • 数组作为外部变量或者静态变量,其元素将被自动初始化为0,自动变量则为无效值。
  • 数组的初始化可以在声明的后面紧跟一个初始化表达式列表,初始化表达式列表用花括号括起来,各初始化表达式之间用逗号分隔。如果省略了数组的长度,则数组的长度为花括号中初始化表达式的个数。如果初始化表达式的个数少于指定的数组元素数,则对于外部变量、静态变量和自动变量来说,没有初始化表达式的元素将被初始化为0。
  • 字符数组的初始化比较特殊:可以使用一个字符串来代替初始化表达式列表。
// 未指定数组长度,以表达式的个数作为数组的长度,即12
int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
printf("%d\n", sizeof(days) / sizeof(int)); 

// 表达式的个数少于指定的索引数,没有初始化表达式的元素,初始值为0
// 相当于:{1, 2, 3, 4, 5, 0, 0, 0, 0, 0}
int nums[10] = {1, 2, 3, 4, 5};
for (int i = 0; i < 10; i++) 
{
    printf("%d", nums[i]);
    if (i == 9) {
        printf("\n");
    } else {
        printf(",");
    }
}
// 字符数组的初始化,下面两个语句是等价的
char s1[] = "ould";
char s2[] = {'o', 'u', 'l', 'd'};

《K&R》02-类型、运算符与表达式

数据类型

  • char 字符型,占用一个字节(8位)
  • int 整型,shrot int通常是16位,long int通常是32位,int可以是16位也可以是32位。类型限定符signed与unsigned可以用于限定char类型或任何整型。
  • float, double, long double 浮点型

常量

  • 整型常量(如1234)默认为int型,long类型常量以字母l或L结尾(如123456789L),无符号常量以u或U结尾。后缀ul或UL表示unsigned long类型。
  • 没有后缀的浮点数为double类型,后缀f或F表示float类型,后缀l或L表示long double类型。
  • 此外可以用0开头表示8进制,以0x或0X开头表示16进制。8进制和16进制也可以用L表示long类型,用U表示unsigend类型。
  • 字符常量书写时将一个字符括在单引号中(如’x’), 可以像整数一样参与数值运算。
  • 字符串常量(如”hello”), 本质上是一个字符数组。字符串的内部以空字符’\0’作为字符串的结尾,因此存储字符串的物理存储单元数要比双引号中的字符数多一个。strlen(s)返回的字符换长度不包括末尾的’\0’。
  • 枚举常量 enum

// 默认从0开始,即FALSE = 0, TRUE = 1
enum
{
    FALSE,
    TRUE
};
// 依次指定
enum escapes
{
    BELL = '\a',
    BACKSPACE = '\b',
    TAB = '\t',
    NEWLINE = '\n',
    VTAB = '\v',
    RETURN = '\r'
};
// 指定部分 FEB值为2, MAR为3,以此类推
enum months
{
    JAN = 1,
    FEB,
    MAR,
    APR,
    MAY,
    JUN,
    JUL,
    AUG,
    SEP,
    OCT,
    NOV,
    DEC
};

声明

  • 自动初始化:外部变量及静态变量会被初始化为0,自动变量(局部变量)的值为未定义值(即无效值)。
#include <stdio.h>

int m;

int main()
{
    int n;
    printf("%d-%d", m, n); // 0-2130567168

    return 0;
}
  • const限定符限定指定变量的值不能被修改,对数组而言const指定数组所有元素的值不能被修改。
int main()
{
    const int i = 1;
    i = 2; // error: assignment of read-only variable 'i'

    return 0;
}

类型转换

  • 自动转换:一个运算符的几个操作数类型不同时,“比较窄的”类型自动转换为“比较宽的”类型。
    • 转换规则:int -> long -> float -> double -> long double
    • char与short类型的操作数参与运算将被转换为int
  • 强制类型转换:(类型名)表达式
char c = '1';
printf("%d\n", sizeof(c)); // 1
printf("%d\n", sizeof(c + c)); // 4

short s = 2;
printf("%d\n", sizeof(s)); // 2
printf("%d\n", sizeof(s + c)); // 4