c语言基础

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

取模运算符%不能应用于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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值