c语言基础

    技术2026-09-15  7

    整数除法会截断结果中的小数部分.

    取模运算符%不能应用于float和double类型

    逻辑运算符&&与||有一些较为特殊的属性,由&&与||连接的表达式按从左到右的顺序进行求值,并且,在知道结果值为真或假后立即停止运算。

    /* atoi: convert s to integer */ int atoi(char s[]) {    int i,n;    n = 0;    for(i = 0; s[i] >= '0' && s[i] <= '9'; ++i)         n = 10*n + (s[i] - '0');    return n; }

     

    表达式 s[i]- '0' 能够计算出s[i]中存储的字符 所对应的数字值,这是因为'0','1'等在字符集中 对应的数值是一个连续的递增序列

     

    /* lower: convert c to lower case; ASCII only */大写转小写 int lower(int c) { if (c >= 'A' && c <= 'Z') return c + 'a' - 'A'; else return c; }

    c语言的定义保证了机器的标准打印字符集中的字符不会是负值,因此,在表达式中这些字符总是正值。 但是,存储在字符变量中的位模式在某些机器中可能是负的,而在另一些机器上可能是正的。为了保证 程序的可移植性,如果要在char类型的变量中存储非字符数据,最好指定signed或unsigned限定符。

     

    表达式中float类型操作数不会自动转换为double型

     

    当表达式中包含unsigned类型的操作数时,转换规则要复杂一些。主要原因在于,带符号值与无符号值 之间的比较运算是与机器相关的,因为它们取决于机器中不同整数类型的大小。例如,假定int类型占16 位,long类型占32位,那么,-1L<1U, 这是因为unsigned int类型的1U 将被提升为signed long类型;但是 -1L>1UL, 这是因为1L将被提升为unsigned long类型,因而成为一个比较大的正数。

     

    float转int 小数点后去掉 double转float 四舍五入

     

    由于函数调用的参数是表达式,所以在把参数传递给函数时也可能进行类型转换。在没有函数原型的情况下, char与short类型都将被转换为int类型,float类型将被转换为double类型。因此,即使调用函数的参数为char 或float类型,我们也把函数参数声明为int或double类型。

     

    sqrt((double) n) 在把n传递给函数sqrt之前先将其转化为double类型。注意,强制类型转换只是生成一个指定类型的n的值, n本身的值并没有改变。

    在通常情况下,参数是通过函数原型声明的。这样,当函数被调用时,声明将对参数进行自动强制转换。例如 对于sqrt的函数原型 double sqrt(double); 下列调用: root2 = sqrt(2); 不需要使用强制类型转换就可以自动将整数2转换为double类型的值2.0

     

    /*squeeze: delete all c from s */ void squeeze(char s[], int c) {    int i, j;    for(i=j=0; s[i] != '/0'; i+)       if(s[i] != c)           s[j++] = s[i];    s[j] = '/0'; } 由于是j++,++是后缀,所以其中if语句完全等价于下列语句: if(s[i]!= c) {    s[j] = s[i];    j++; }

     

    /* strcat: concatenate t to end of s; s must be big enough */ void strcat(char s[], char t[]) {     int i, j;     i = j = 0;     while (s[i] != '/0') /* find end of s */        i++;     while ((s[i++] = t[j++]) != '/0') /* copy t */        ; } 在将t中的字符逐个拷贝到s的尾部时,变量i和j使用的都是后缀运算符++, 从而保证在循环过程中i与j均指向下一个位置。

     

    按位运算符: c语言提供了6个位操作运算符。这些运算符只能作用于整型操作数,即只能 作用于带符号或无符号char,short,int,long类型: &    按位与(AND) |    按位或(OR) ^    按位异或(XOR) <<   左移 >>   右移 ~    按位求反(一元运算符) 必须将位运算符&,|同逻辑运算符&&,||区分开来,后者用于从左到右求表达式的真值。 例如,如果x的值为1, Y的值为2,那么,x & y的结果为0,而x && y的值为1。

     

    移位运算符<<与>>分别用于将运算的左操作数左移与右移,移动的位数则由右操作数 指定(有操作数必须是非负值)。因此,表达式x<<2将把x的值左移2位,右边空出的2位 用0填补,该表达式等价于对左操作数乘以4。在对unsigned类型的无符号值进行右移位 时,左边空出的部分将用0填补;当对signed类型的带符号值进行右移时,某些机器将 对左边空出的部分用符号位填补(即“算术移位”),而另一些机器则对左边空出部分用0填补 (即“逻辑移位”)。

     

    x = x & ~077 将把x的最后6位设置为0。注意,表达式 x & ~077 与机器字长无关,它比形式为x & 0177700 的表达式要好,因为后者假定x是16位的数值。这种可移植的形式并没有增加额外的开销,因为 ~077是常量表达式,可以在编译时求值。

     

    /* getbits: get n bits from position p */ unsigned getbits(unsigned x, int p, int n) {       return (x >> (p+1-n))& ~(~0 << n); } 其中,表达式x >> (p+1-n) 将期望获得的字段移位到字的最右端。~0的所有位为1,这里用语句 ~0 << n将~0左移n位,并将最右边的n位用0填补。再使用~运算对它按位取反,这样就建立了最右边 n位全为1的屏蔽码。

     

    x *= y+1 的含义是: x = x * (y+1) 而不是 x = x * y + 1 i += 2 运算一次 i = i+2 运算两次

     

    条件表达式:expr1 ? expr2 : expr3 中,首先计算expr1, 如果其值不等于0(为真),则计算expr2的值,并以该值作为条件表达式的值, 否则计算expr3的值,并以该值作为条件表达式的值。 采用条件表达式可写简洁代码。例如,打印一个数组的n个元素,每行打10个,每列之间用一个空格 隔开,每行用一个换行符结束(包括最后一行)。 for (int i = 0; i<n; i++)     printf("%6d%c", a[i], (i%10 == 9 || i = n-1)? '/n' : ' ');

     

    运算符优先级

     

    优先级

    运算符

    名称或含义

    使用形式

    结合方向

    说明

    1

    []

    数组下标

    数组名[常量表达式]

    左到右

     

    ()

    圆括号

    (表达式)/函数名(形参表)

     

    .

    成员选择(对象)

    对象.成员名

     

    ->

    成员选择(指针)

    对象指针->成员名

     

    2

    -

    负号运算符

    -表达式

    右到左

    单目运算符

    (类型)

    强制类型转换

    (数据类型)表达式

     

    ++

    自增运算符

    ++变量名/变量名++

    单目运算符

    --

    自减运算符

    --变量名/变量名--

    单目运算符

    *

    取值运算符

    *指针变量

    单目运算符

    &

    取地址运算符

    &变量名

    单目运算符

    !

    逻辑非运算符

    !表达式

    单目运算符

    ~

    按位取反运算符

    ~表达式

    单目运算符

    sizeof

    长度运算符

    sizeof(表达式)

     

    3

    /

    表达式/表达式

    左到右

    双目运算符

    *

    表达式*表达式

    双目运算符

    %

    余数(取模)

    整型表达式/整型表达式

    双目运算符

    4

    +

    表达式+表达式

    左到右

    双目运算符

    -

    表达式-表达式

    双目运算符

    5

    <<

    左移

    变量<<表达式

    左到右

    双目运算符

    >>

    右移

    变量>>表达式

    双目运算符

    6

    >

    大于

    表达式>表达式

    左到右

    双目运算符

    >=

    大于等于

    表达式>=表达式

    双目运算符

    <

    小于

    表达式<表达式

    双目运算符

    <=

    小于等于

    表达式<=表达式

    双目运算符

    7

    ==

    等于

    表达式==表达式

    左到右

    双目运算符

    !=

    不等于

    表达式!= 表达式

    双目运算符

    8

    &

    按位与

    表达式&表达式

    左到右

    双目运算符

    9

    ^

    按位异或

    表达式^表达式

    左到右

    双目运算符

    10

    |

    按位或

    表达式|表达式

    左到右

    双目运算符

    11

    &&

    逻辑与

    表达式&&表达式

    左到右

    双目运算符

    12

    ||

    逻辑或

    表达式||表达式

    左到右

    双目运算符

    13

    ?:

    条件运算符

    表达式1? 表达式2: 表达式3

    右到左

    三目运算符

    14

    =

    赋值运算符

    变量=表达式

    右到左

     

    /=

    除后赋值

    变量/=表达式

     

    *=

    乘后赋值

    变量*=表达式

     

    %=

    取模后赋值

    变量%=表达式

     

    +=

    加后赋值

    变量+=表达式

     

    -=

    减后赋值

    变量-=表达式

     

    <<=

    左移后赋值

    变量<<=表达式

     

    >>=

    右移后赋值

    变量>>=表达式

     

    &=

    按位与后赋值

    变量&=表达式

     

    ^=

    按位异或后赋值

    变量^=表达式

     

    |=

    按位或后赋值

    变量|=表达式

     

    15

    ,

    逗号运算符

    表达式,表达式,…

    左到右

    从左向右顺序运算

     

     

    注意,位运算符优先级比运算符==与!=的低。这意味着,位测试表达式,如 if((x & MASK) == 0)... 必须用圆括号括起来才能得到正确结果。 同大多数语言一样,c没有指定同一运算符中多个操作数的计算顺序(&&,||,?:和,运算符除外),例如 x = f() + g(); f()可以在g()之前计算,也可以在之后,因此,当函数f或g改变了另一个函数所使用的变量,那么x的结果 可能会依赖于这两个函数的计算顺序。为了保证特定的计算顺序,可以把中间结果保存在临时变量中。 类是的,c也没指定函数各参数的求值顺序。因此,下列语句 printf("%d %d/n", ++n, power(2, n)); /* 错 */ 在不同编译器中可能会产生不同的结果,这取决于n的自增运算在power调用之前还是之后。 a[i] = i++; 也是这种情况,数组下标i 是引用旧值还是引用新值? 所以在任何一种编成语言中,如果代码的执行结果与求值顺序有关,则都是不好的程序设计风格。在不了解 各种机器是如何解决的,就最好不要尝试运用某种特殊的实现方式。

     

    函数:

    #include <stdio.h> #define MAXLINE 1000 /* maximum input line length */ int getline(char line[], int max) int strindex(char source[], char searchfor[]); char pattern[] = "ould"; /* pattern to search for */ /* find all lines matching pattern */ main() {     char line[MAXLINE];     int found = 0;     while (getline(line, MAXLINE) > 0)         if (strindex(line, pattern) >= 0) {             printf("%s", line);             found++;         }         return found; } /* getline: get line into s, return length */ int getline(char s[], int lim) {     int c, i;     i = 0;     while (--lim > 0 && (c=getchar()) != EOF && c != '/n')     s[i++] = c;     if (c == '/n')         s[i++] = c;     s[i] = '/0';     return i; } /* strindex: return index of t in s, 1 if none */ 写的好啊 两个for循环就搞定了 int strindex(char s[], char t[]) {     int i, j, k;     for (i = 0; s[i] != '/0'; i++) {         for (j=i, k=0; t[k]!='/0' && s[j]==t[k]; j++, k++)             ;         if (k > 0 && t[k] == '/0')             return i;     }     return -1; }

     

    外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。 例如,如果main,sp,val,push与pop是依次定义在某个文件中的5个函数或外部变量,如 下所示: main() { ... } int sp = 0; double val[MAXVAL]; void push(double f) { ... } double pop(void) { ... } 那么,在push与pop这两个函数中不需要进行任何声明就可以通过名字访问变量sp与val, 但是,这两个变量名不能用在main函数中,push与pop函数也不能用在main函数中。 外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度。 用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。 通过static限定外部对象,可以达到隐藏外部对象的目的。 外部的static声明通常多用于变量,当然,它也可用于声明函数。通常情况下,函数名字是全局可 访问的,对整个程序的各个部分而言都可见。但是,如果把函数声明为static类型,则该函数名 除了对该函数声明所在的文件可见外,其他文件都无法访问。 static也可用于声明内部变量。static类型的内部变量同自动变量一样,是某个特定函数的局部变量, 只能在该函数中使用,但它与自动变量不同的是,不管其所在函数是否被调用,它一直存在,而不像 自动变量那样,随着所在函数的被调用和退出而存在和消失。换句话说,static类型的内部变量是一 种只能在某个特定函数中使用但一直占据存储空间的变量。 register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在 机器的寄存器中,这样可以使程序更小,执行速度更快。但编译器可以忽略此选项。 实际使用时,底层硬件环境的实际情况对寄存器变量的使用会有一些限制。每个函数中只有很少的变 量可以保存在寄存器中,且只允许某些类型的变量。但是,过量的寄存器声明并没有什么害处,这是 因为编译器可以忽略过量的或不支持的寄存器变量声明。另外,无论寄存器变量实际上是不是存放在 寄存器中,它的地址都是不能访问的。 在一个好的程序设计风格中,应该避免出现变量名隐藏外部作用域中相同名字的情况,否则,很可能 引起混乱和错误。 在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初 值则没有定义(即初值为无用的信息)。 对外部变量和静态变量来说,初始化表达式必须是常量表达式,且只初始化依次(从概念上讲是在程序 开始执行前进行初始化)。对于自动变量与寄存器变量,则在每次进入函数和程序块时都将被初始化。 数组初始化: int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 当省略数组的长度时,编译器将把花括号中初始化表达式的个数作为数组的长度,在本例中长度为12。 如果初始化表达式的个数比数组元素少,则对外部变量,静态变量和自动变量来说,没有初始化表达式 的元素将被初始化为0,如果初始化表达式的个数比数组元素多,则是错误的。不能一次将一个初始化式 指定多个数组元素,也不能跳过前面的数组元素而直接初始化后面的数组元素。 字符初始化比较特殊。 可以char pattern[] = "ould "; 等价于char pattern[] = { 'o', 'u', 'l', 'd'}; 这种情况下,数组长度5。4+一个字符串结束符'/0' 递归并不节省存储器开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。递归的执行 速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解。在描述树等递归定义 的数据结构时使用递归尤其方便。 很自然,如果某个包含文件的内容发生了变化,那么所有依赖于该包含文件的源文件都必须重新编译。 较长的宏定义可分为若干行,这是需要在待续的行末尾加上一个反斜杠符/。#define 指令定义的名字 的作用域从其定义点开始,到被编译的源文件的末尾处结束。 带参数宏定义 #if SYSTEM == SYSV #define HDR "sysv.h" #elif SYSTEM == BSD #define HDR "bsd.h" #elif SYSTEM == MSDOS #define HDR "msdos.h" #else #define HDR "default.h" #endif #include HDR c语言专门定义了两个预处理语句#ifdef与#ifndef,它们用来测试某个名字是否已经定义。

     

     

    最新回复(0)