标准C基础知识笔记一

12年前


Linux就是用c和c++来编写的,但是两者有区别,如何区分呢
先C,再C++
vi xxx.c,其扩展名为.c
 用c语言的格式来写的,不是用脚本语言来写的
 .c文件是源程序,不是可执行文件
 通过gcc来进行翻译成相应的机器指令a.out文件
 gcc  xxx.c  a.out

1\编辑vi xxxx.c===》可移植
2\编译gcc xxxx.c a.out,编译,连接产生可执行文件a.out

gcc -c xxxx.c xxxx.o(编译)
gcc xxx.o 产生a.out(连接)
 
3\执行a.out===》已经生成某个平台的机器码==》不可移植

可执行文件的格式是不一样的:PE\COFF\ELF

C语言第一课:
mkdir c1
1、编辑第一个c语言源文件
vi hello.c

main()
{
}

2、编译gcc hello.c
3、执行./a.out

4、调整环境变量
echo $PATH
PATH=$PATH:.
5、执行a.out

 

vi .bashrc

编辑.bashrc文件

两个方向C,C++;要分清哪个是C的,哪个是C++的

vi xxx.c保存退出;是c程序的源程序
gcc xxx.c编译,连接
./a.out执行


mkdir c1
cd c1
vi hello.c

main()
{
}

gcc hello.c==>gcc -c hello.c会产生一个hello.o,gcc hello.o会产生一个a.out文件
   gcc hello.o -ohello(通过-o可以指定一个文件名)
   gcc hello.c -ohehe
./a.out

vi hello.c
#include <stdio.h>  //头文件,是一个辅助文件
int main()
{
 printf("hello world! \n"); //c语言中的输出函数
#include "xxx"
 return 0;
}

//ls  /usr/stdio标准c自带的h文件


C语言程序的结构

1、#开头的叫预处理行;用<>号文件里的内容放在#行位置;可以有多行
 预处理行必须写在一行,如果在一行写不开的话,必须用续行符\来进行连接(表示下一行和这一行实际上是同一行)
 如#include \
  <stdio.h>
  <stdio.h>是标准C自带的
2、int main()
  {
 printf("hello,world\n");
#include "xxx"  //切记include后面用<>的话表示其内的是标准c的,如果是include自己的文件需要用“”

   }
一个C语言程序必须有这一部分;主函数
3、在c语言中,表达完整的意思用语句并以;结尾

\转义字符;\n换行;\r回车;\t TAB;\b退格;\\表示一个反斜杠字符

4、return 0;0与main方法中的int相呼应
  return 0;一般情况下可以这样理解:带回0表示一切正常;其他的数字则表示有错误
  return 0;这行也可以不写
5、注释:/*.......*/
//main函数是一个C程序中必备的而且唯一的

xxx的内容
 printf("hello,c");
 printf("hello,java");
 
改错一定要改第一个错误

int 整数 0,5,100
char 字符 用单引号表示如'a','*','8'
float单精度(一般7位有效数字),double双精度(一般15位有效数字),long double(非标准的)  小数3.5,4.9,

整数在计算机中是怎么表示的呢?(二进制)
00000000    (8位)   用权重最大的那位作为符号位;0的时候表示非负数;1表示正数
什么是权重(理解);
-128---127

补码:正数的补码为原码,负数的补码为正数的原码各位取反再加1
正数直接用原码来表示;负数用补码来表示

unsigned int非负数,没有符号位,0-255


ASCII码 用0--127
'a'--'z'  97-122
'A'--'Z'  65--90
'0'--'9'  48--57
'\'-->13  '\n'--10  '\t'--9
nul空字符--->0   '空格'---32

字符当整数用的时候,用的是编码;如‘0’+20=68


char最多是7位二进制就可以来表示,计算机管理存储单元是以字节为单位;1个字节8位(二进制位)
一个汉字GB2312就是2个空闲的字符来表示;1个字符占用1个字节的位置

整数int用4个字节,32个二进制位来表示;1个表示符号位,31个表示数值;最大正数2的31次方-1;最小的负数为-2的31次方-1
short int短整数,系统只分配2个字节的空间来存储-32768---32767;判断一个数字是否偶数还是奇数只判断存储二进制的最为一位是奇数还是偶数即可。
unsigned short int 0-65535
long int长整数,4个字节
long long 8个字节,-2的63次方----2的63次方-1
unsiged longlong  2的64次方-1

小数  表示近似表示不精确
 float单精度小数;4个字节   符号位1,阶码8,尾数23;
 double  8个字节,符号位1,阶码11,尾数52
 
sizeof某种类型用几个字节来表示
超出范围溢出,上溢出或下溢出;
  对于整数而言,溢出会导致回绕
  对小数而言,溢出不会回绕,会变成无穷,正负无穷inf
3.5f
3.5d
3.5L

C真假:非0为真,0表示假(‘\0’,0.0,null)
c++另外还有个false为假

自动类型提升:
 char,short---》int
 float----》double
 
如何来输出各种类型的数据
#include  <stdio.h>
int main()
{
 printf("hello,data\n");
 printf("aaa%dbbb%fccc\n");//%格式化输出;占位符;正常的情况下
 pinrtf("aaa%ibbb%fccc%cddd\n",123,45.6,65);
 
}

%i/%d/:int
%hd/%ld: short int /long int
%u: unsigned int
%f: float/double
%g: float/double去掉了尾随的0
%c:char
%s:“string”,用双引号引起来的东西
%p:address
%x/%o:十六进制/8进制

n进制表示
数字/n取余;然后商/n取余,直到结束

16进制用0x开头
8进制用0开头

比较并了解:如何看printf在c语言的那个库中
1、man printf
2、man -a printf
3、man 3 printf

  stdio.h==standard input/output headerfile


变量===二进制位,一部分内存空间
变量名  
 名字:
  1、只能有字母、数字和下划线,数字不能在前 
  2、不能用关键字
  3、不能用函数名
  4、尽量做到见名知意
  5、严格区分大小写
  定义一个变量,开始里面表示着一个不可预知的数据,垃圾数据==>指定初始值就不会有垃圾数据了

mkdir c2
vi init.c  //进一步来验证垃圾数据或指定值初始化
#include <stdio.h>
int main()

 double income;
 short int pen,notebook;
 int age=40;//指定值初始化
 double salary=1234.56,weight=78.9;
 printf("income=%lf\n",income);
 printf("pen=%hd,notebook=%hd\n",pen,notebook);
 printf("");
 return 0;
}

vi的环境参数文件;可以在vi下看set all
vi ~/.exrc
set nu
set ts=4


变量不是喜新厌旧而是见新忘旧
变量类型不需要重复说明,以后用的时候只需要用名字即可


#include <stdio.h>

int main()

 int n=12345.6;
 printf("n=%d\n",n);  //最终显示为12345
 printf("n=%f\n",n);  //
 return 0;
}

强制类型转换:用()把某个数值转成其他类型的数值,但其类型并不转换

变量定义必须放在最前面

计算某个类型的变量需要占多少个字节sizeof(只关心类型)
vi sizeof.c
#include <stdio.h>
int main()
{
 int n=10;
 printf("%i\n",sizeof(int));
 printf("%i\n",sizeof(n));
 printf("%i\n",sizeof(n+5));
 printf("%i\n",sizeof(n+5.0)); //转换成double
 printf("%i\n",sizeof(n=123)); //sizeof只关注类型,不关注其数值
 printf("n=%i\n",n)
 return 0;
}


我们看下sizeof的汇编代码
gcc -S sizeof.c
会产生一个sizeof.s的汇编语言的文件
gcc sizeof.s 也会产生一个a.out文件,和前面的一样

vi sizeof.s


esp栈顶指针,我们传输的数据都放到栈里了

通过这个例子可以查看c程序与汇编程序;

 

不变的变量称为常量;constant
不变的数值称为字面量

const double PI=3.14

vi consant
#include <stdio.h>
int main()
{
 const double PI=3.14;
 const int i=10;
 printf("PI=%g,i=%d\n",PI,i);
 PI=3.15;//可能会出现编译error或警告:向只读变量PI赋值
 return 0;

}
编译的时候报程序中有游离的\错误:一般解决方案是里面存在中文的字符;比如
空格及标点符号等;需要换成英文的;看光标是不是变成2个字符的宽度了。

除了注释和双引号里面,其他地方都不能出现中文的字符

C里面还支持宏#define CLASS “sd1109”
#define CLASS “sd1109”意思是遇到CLASS换成sd1109
#define DATE 20110910 意思是遇到DATE换成日期
#define AA PI*
#define BB 10+i


printf("%s,%d,CLASS,DATE");
printf("%g\n",AA BB);
编译的时候线做替换
gcc -E consant.c

对于常量变更时,尽量用const,少用define
常量必须初始化赋值

 

+ - * / % ><  !=   ==  >=  <=
/取得商
%取得余数


C里面没有true或false


vi logic.c
#include <stdio.h>
int main()
{
 int a=3,b=5;
 printf("%d,%d,%d\n",a<=b,a>=b,a>=3);
 a=-3;
 printf("%d,%d\n",a>=0 && a<=100,0<=a<=100);//前面不成立后面就不用考虑了。
 //在这里计算机会怎么来分析0<=a<=100呢?==》首先要看0与a对比不论如何都是0或1,所以0或1再与100相比,肯定成立
 
 printf("%d,%d\n",a<0||a>100,b<0||b>100);//||前面条件成立的话短路
 int y=2012;
 printf("%d\n",!(y%4==0&&y%100!=0||y%400==0));
 return 0;
}


&按位与  用途:测试某位是0还是1;
   只有两个都是1才是1,只要有一个是0则为0
  
|按位或   有一个1则为1,其他为0     用途:把某个位置设置为0或1
^异或  两者相同为0,不同为1     0和A异或A值不变,1和A异或A值反过来了
~   按位取反  20按位取反  20  00000000 00000000 00010100
      取反  11111111 11111111 11101011
      
 验证下:vi bits.c
  #include <stdio.h>
  int main()
  {
   printf("%d,%d,%d\n",~20,~1,~0);
   return 0;
  }
  -21,0,-1
  
  结论:补码:负数   正数二进制表示按位取反+1
>>   左移的时候右边空位补0
<<   右移左空补符号位

在运算过程中,变量的值只有在赋值运算时,才发生变化

#include <stdio.h>
  int main()
  {
   printf("%d,%d,%d\n",~20,~1,~0);
   unsigned int n=-1;
   printf("%u,%x,%d\n",n,m);
   int m=-1;
   printf("%x,%x\n",n>>3,m>>3);
   printf("%x,%x\n",n,m);
   printf("%x,%x\n",n<<3,m<<3);
   m=300;
   printf("%d,%d\n",m<<3,m>>3);
   m=0x3862517b;
   printf("%x\n",(char)m);
   printf("%x\n",(char)(m>>16));
   int a=0x12,b=0x34,c=0x56,d=0x78;
   printf("%x\n",(a<<24)|(b<<16)|(c<<8)|d);
   
   
   return 0;
  }
  
  
 地址运算符&
 每一个变量,系统都会分配一部分内存空间。那么这部分内存地址在哪里呢?
 通过地址运算符来进行定位这部分内存地址

 & 根据变量或常量取地址  *根据地址找到对应的变量或常量
 *&a====》还是a
 
 vi address.c
 #include <stdio.h>
 int main()
 {
  int a;
  int b;
  printf("&a=%p,&b=%p\n",&a,&b);
  (*&a)=100;(*&b)=888888;
  printf("a=%d,b=%d\n",a,b);
  return 0;
  
 }
 一个变量有多个编号时,以最小的地址编号的为准作为变量的地址。
 
 xxx?yyy:zzz 三目运算符;xxx判断是否成立,真用yyy,假用zzz
 
 ,运算符,把多个运算连在一起,是一个双目运算符
 t=a;a=b;b=t;
 
 t=a,a=b,b=t;
 ,有些情况下也不是运算符,而是分隔符
 
 如何区分是运算符还是分隔符呢?
 ,运算符是以最后一个,为计算结果
 
 
 n=n>>3   a=a+50   a=a^b
等价n>>=3    a+=50    a^=b
 运算和赋值合起来(组合赋值)
 类似的还有:-=  *= /= %= &= |= <<=
 
 
 ++a   --a
 
 #include <stdio.h>
 int main()
 {
  int a,b,c,d;
  a=b=c=d=0;//连续的赋值从右往左算;
  a++;++b;
  printf("%d,%d\n",a,b);
  return 0;
  
 }
 
 a++;++a的区别在于:运算的结果;a++拿变量的旧值做结果(后++);++a拿变量的新值作为结果(前++)
 a++<10;解释:会对a进行++运算,但只会拿a的旧值与10进行比较;可以这样理解:temp=a,a=a+1,temp<10;
 
 vi a.c
 
 #include <stdio.h>
 int main()
 {
  int a=0;
  int b=0;
  
  printf("%d %d %d\n",b=a+++(a+++2),b,a=b++);
  printf("%d %d\n",a,b)
 }
 
 gcc a.c
 a.out
 
 转换成汇编
 gcc -S a.c
 vi a.s
 
 
 ebp32位的BP ,ebp是基址指针   EbP与bp的关系就象AX与AL,AH的关系.   
 BP为基指针(Base Pointer)寄存器,用它可直接存取堆栈中的数据,它的作用是在调用函数时保存ESP使函数结束时可以正确返回;
 
 EAX—EDX可称为数据寄存器,你除了直接访问外,还可分别对其高十六位和低十六位进行访问。
 它们的低十六位就是把它们前边儿的E去掉,即EAX的低十六位就是AX。而且它们的低十六位又可以分别进行八位访问,
 也就是说,AX还可以再进行分解,即AX还可分为AH(高八位)AL(低八位)。
 
 运算本身一般不会改变变量本身的值,只有几个是改变变量的值:前++,前--
 
 int a=9,b=2;===>a/b=4
 
 变量类型终身不变(double)a/b,实际上a还是int变量,只是其数值变成了double类型来参与运算
 在比较两个小数是否相等的时候,不能用==只能用近似相等fabs(a-b)<1E-5
 
 避免在一个语句里面使变量的值多次改变。
 
 
 输出:把内存中的数据显示到屏幕上;
 
 输出printf("格式串",...);格式占位符
 输入scanf("格式串",地址表....);在这里的格式串只有占位符没有其他东西
 
 #include <stdio.h>
 /*
 %c字符
 %d,%x,%o:十进制、十六进制、八进制
 %ld,%hd:longint 、short int
 %f:float
 %lf:double
 %s:字符串
 */
 int main()
 { 
  int n=0;//准备好存储的地方
  float f=0;
  double d=0;
  char c=0;//'\0'  表示空字符,空字符的数值是0
  printf("请输入一个整数和一个小数:");
  scanf("%d%f",&n,&f);//只需要格式占位符,不需要其他的;如
  printf("%d,%g",n,f);
  scanf("%lf\n",&d);//X
  //不要有\n,否则在后期运行的时候会出问题。格式串只要不是空白什么都行
  //运行的时候跳过所有的空白字符,到遇到非空白字符为止
  
  
  scanf("%c",&c);
  printf("c=[%c](%d)\n",c,c);
  return 0;
 }
 //输入的信息一般用空白字符隔开即可,比如空格,tab
 
 如果在格式串中输入了非空字符,则要求用户在输入时必须是原样输入
 ================================================================
 
  scanf也会带回数据,成功的输入了几个数据。
  printf("成功项数:%d\n",scanf("%f,%lf",&f,&d));