标准C基础知识笔记四

12年前


 定义数组要指定元素的个数(固定的)
 数组名代表什么数组,当作为数值来用时,表示数组第一个元素;
 下标一定比元素个数少1
 尽量避免越界
 一般来说多维能解决的问题,用一维数组也能解决
 *(a+i);//这个是地址的加法  相当于a+i*sizeof(int)

 对于char数组可以存放字符串,但是必须有\0结尾标志

 ^D,输入结束


 strcmp();用来比较两个字符串
 strcpy只能用于字符串赋值
 strct在某一个字符串追加一个字符串
 “字符串”是存储在只读存储器中的,只有系统能访问不能修改。===》如果修改的话,会出现段错误
 strlen(...)
 strcmp(...,...)  相等0,左边大为正,左边小为负(比较的是ascii码)
 strchr  从左往右查找字符串
 strrchr 从右往左查找字符串
 strstr 从字符串中查找字符串

 结构的对齐与补齐
 联合(共用体)
 函数

 vi struct.c

 #include <stdio.h>
 typedef struct Goods{
  char name[18];//18个字节
  double price;//8个字节
  char special;//1
  int num;//4个字节
  short saled;//2个字节
 }gs;
 int main()
 {
  gs a[10];//定义一个Goods的变量数组,不用初始化,可以通过键盘来输入
  printf("%d\n",sizeof(gs));//查看gs类型占用多少字节的存储空间
  //对齐;一般来说一个变量分配空间的时候都会考虑分配内存边界上,
  //一个变量的占用的内存地址,一般是这个变量占用的字节的倍数;
  //虽然这样看起来有点浪费,但是访问起来速度快
  //char随便一个地址就可以访问;short为2的倍数,int为4的倍数;double为8的倍数
  //一般编译器超过4,也按照4的倍数来访问

  return 0;
 }

 对齐规则:每个成员都从本身长度的倍数开始
 -------------------------------------------------------
 | name 18 | 空2|price 8|special 1| int num 4|    空 4|
 -------------------------------------------------------

 补齐:是为了多个元素挨着摆放时,也能满足对齐规则

 为什么要对齐和补齐呢?(不同的OS,其寻址是不一样的)
 32位的操作系统在访问数据的时候都是从内存中按照4的倍数来访问数据。

 数组的元素的内存存储地址不一定是连续的,可能存在间隙;


 位断:
 由于内存分配是按照几个字节来分配的。但个别时候可能只需要几个位即可;C语言可以设置为某几个
 成员用几个位

 //一般会用在嵌入式开发中。每一个位控制一个东西,控制一个管脚
 #include <stdio.h>

 struct mode{//比如用户的读写执行权限rwxrwxrwx
  unsigned char userread:1;
  unsigned char userwrite:1;
  unsigned char userexecute:1;
  unsigned char groupread:1;
  unsigned char groupwrite:1;
  unsigned char groupexecute:1;
  unsigned char otherread:1;
  unsigned char otherwrite:1;
  unsigned char otherexecute:1;
  unsigned char f3:3;
  unsigned int f10:10;
 };
 int main()
 { 
  struct mode m={1,1,0,1,0,0,1,0,0,6,999};//初始化
  printf("size:%d\n",sizeof(m));
  printf("%d%d%d,%d%d%d,%d%d%d",
  m.userread,m.userwrite,m.userexecute,
  m.groupread,m.groupwrite,m.groupexecute,
  m.otherread,m.otherwrite,otherexecute
  );
  printf("%d,%d\n",m.f3,m.f10);
  
  return 0;
 }

 //备注:在vi下替换某几行内的某个字符串user为另一个字符串other
 //   :10,12s/user/other/

 
 
 联合union
 所有成员共用一块地方(共用体),操作任何一个都会影响其他的。
 那么什么时候用联合呢?这些成员总是只用一个。

 #include <stdio.h>
 union IP{
  char i4[4];
  int net;
   //可以使用192.168.x.x格式来表示IP;也可用一个整数来表示一个IP地址
 };

 int main()
 {
  printf("sieze:%d\n",sizeof(union IP));
  IP me{192,168,0,20};
  printf("%x\n",me.net);
  
  me.net=0x12345678;
  int i;
  for (i=0;i<4;i++)
   printf("%d ",me.i4[i]);
  printf("\n")
  return 0;
  
 }
 
 不同系统,其存储方式也是不一样的,有的是小端存储有的是大端存储
 
 C语言本身就是一个函数语言;把实现某一个功能的打个包==》函数
 
 printf scanf strcpy strlen strcat....标准C里面的标准库函数
 
 vi function.c
 #include <stdio.h>
 
 int main()
 {
  int n=Main(); //Main()带回一个数值10
  printf("hello world!\n");
  n+=Main();
  return 0;
 }
 
 int Main()//函数名是区分大小写的
 {
  printf("你好 世界\n");
  return 10;
 }
 
 一个C语言只有一个main函数,也是程序的入口,可以在main函数内调用其他函数。
 
 写一个函数清输入缓冲区
 
 #include <stdio.h>
 
 void clear();//函数声明;
 int main()
 {
  char name[20];
  char gender;
  printf("请输入姓名:");
  scanf("%s",name);
  clear();//这个决定了要把void clear()函数放到前面;负责会出现隐式声明错误;
  printf("请输入性别(M/F):");
  scanf(" %c",&gender);//理解为什么在" %c"有一个空格;因为上面输入姓名之后肯定要敲个回车来确定输入。但这个回车会留在输入缓冲区中;会影响下面的输入。
  //所以要加个空格来解决这个问题
  printf("%s:%c\n",name,gender);
 }
 
 void clear()//void代表这个函数不带东西回来;函数体
 {
  scanf("%*[^\n]");
  scanf("%*c");
  
 }
 C语言中对于不认识的类型,一般会默认的为int类型(隐式声明)


 函数定义:函数名、函数声明、函数体
 函数调用:
 
 
 从键盘输入一个指定范围内的整数(get5,80)
 
 #include <stdio.h>
 int get(int a,int b)//形参(定义的时候使用)
 {
  printf("请输入两个整数:\n");
  printf("a=%d,b=%d\n",a,b);
  int n;//这里的n与main里面的n是不同的n。在不同的{}号的是不同的。
  do{
   printf("请输入%d到%d的一个整数:",a,b);
   //scanf("%d",&n);//切记:在这里%d后面没有\n
   if(scanf("%d",&n)!=1){
    scanf("%*[^\n]*c");
    //进一步判断输入的a<b,如果a>b,则需要交换数值
    //判断输入的是整数而非小数或字符    
   }
  }while(n<a||n>b);

  return (a+b)/2;
 }
 int main()
 {
  int n=get(1,100);//实参(调用的时候使用)
  int m=get(10,50);
  printf("n=%d,m=%d\n",n,m);
  return 0;
 }
 
 
 ******************************************************
 
 
 写一个函数在多个程序里面能够使用(函数调用);
 ------------------------------------
 编写一个程序里面带clear函数
 vi  clear.c
 #include <stdio.h>
 void clear(){
  scanf("%*[^\n]");
  scanf("%*c");
 }
 
 
 编写一个声明clear函数
 vi  func_clear.h
 
 void clear();//前面已经写了一个clear函数,在这里只是写一个函数声明
 
 
 在写的程序里面调用这个函数
 vi param.c
 #include <stdio.h>
 #include "func_clear.h"  //函数的声明
 int main()
 {
  clear();
  return 0;
 }
 
 
 //把声明插入一份就行了。

 一个程序两个文件怎么来编译呢?==》分开编译,连接
 编译:
 gcc -c clear.c  //只编译
 gcc -c param.c
 gcc clear.o param.o//合在一起,谁前谁后无所谓

 
 man gcc
 
 
 每一个头文件感兴趣就可以man下看看到底是怎么回事。
 一般.h文件都在/user/include目录下
 
 
 //写一个小程序来观看函数调用
 //输入一行文字
 //将其中的数字转换成对应的汉字,小写字母变成大写,其他不变
 
 vi  check.c
 #include "check.h" //把函数声明插进来
 bool isLower(char c)
 {
  return(c>='a'&&c<='z');
 }
 
 bool isNum(cha c)
 {
  return(c>='0'&&c<='9');
 }
 
 vi check.h
 //函数声明
 typedef enum bool{true=1,false=0}bool;//c语言里面并没有bool类型,定义一个类型
 bool isLower(char c);
 bool isNum(char c);
 
 转换函数
 
 vi  convert.c
 #include <stdio.h>

 char toUpper(char c) //把小写转换成大写
 {
  return(c-'a'+'A');  //c-'a'字符在字母表中的偏移量;+'A'在大写字母中的偏移量
 }
 
 char* toChinese(char c) //汉字是一串字符,而非一个字符;操作字符串,只要知道数组的开始地址即可。
 {
  char num[10][5]={"零","一","二","三","四","五","六","七","八","九"};
  //这样定义的含义?10个一维数组,每个一维数组
  printf("%s",num[c-'0']);
  return(num[c-'0']);//基本上没作用;变量是有范围的
  //'0',数字为48;c-'0'
 }
 
 vi convert.h
 
 char toUpper(char);
 char* toChinese(char);
 
 
 
 #include <stdio.h>
 #include "check.h"
 #include "convert.h"
 int main()
 {
  char c;
  printf("请输入一行文字:");
  do{
   scanf("%c",&c);
   if (islower(c))
    printf("%c",toupper(c));
   else if(isnum(c))
    printf("%s",tochinese(c));
   else
    printf("%c",c);
  }while(c!='\n');
  return 0;
 }
 
 
 vi charaddr.c
 #include <stdio.h>
 
 int main()
 {
  char a[100]="good afternoon,everyone!";
  char* p=a+5;//a+5===>&a[5],在这里两者是等价的
  printf("%s\n",p);
  return 0;
  
 }
 
 问题:
 //判断某年有多少天
 //日期是否有效日期;
 //日期是这一年的第几天
 //日期是这一年的第几周的哪一天
 
 函数:
  单一定义原则;
  函数多次声明;
  
常用的C语言标准库文件  
 stdio.h  //输出输入
 stdlib.h //工具
 time.h  //时间相关
 math.h  //数学相关
 string.h  //字符和内存操作
 ctype.h  //字符操作
 
 
 
 局部变量和全局变量
 
 #include <stdio.h>
 #include <string.h>
 
 char w[7][10]={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
 //这种变量为全局变量(无什么范围限制;在C里面先声明后使用)
 
 //也可以使用extern来声明,告诉编译器,w在其他地方有定义;
 
 //extern char w[7][10];
 char* week(int);
 char* weekday(int n)
 {
  return w[n];  
 }
 int main()
 {
  printf("%s\n",weekday(3));
  strcpy(week(5),"fr");//通过week(5)知道其地址,然后重新赋值
  printf("%s\n",week(5));
  return 0;
 }
 
 vi weekday.c
 #include <stdio.h>
 //下面这句配合extern来使用
 char w[7][10]={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
 char e[7][10]={"Sunday","Monday",...};
 //通过这种方式也可以实现多语言的支持
 //static h[7][10]={,,,,};//static限制全局变量只能在本文件中使用,对外隐藏
 
 void showweekday(int n)
 {
  printf("%s\n",weekday(5));
  printf("%s\n",e[2]);
  return 0; 
 }
 char* week(int n)//构建一个函数,让外界通过调用这个函数来访问static全局变量
 {
  return h[n];
 }
 文件本身并不限制全局变量的范围;static可以限制全局变量只能在本文件中使用
 但是其他文件怎么能访问这个static的全局变量呢,可以构建一个函数,通过调用函数的方式即可
 strcpy(week(5),"fr");
 全局函数只能在本文件中使用也是加static
 
 
  #include <stdio.h>
 #include <string.h>
 
 
 char* weekday(int n)
 {
  static char w[7][10]={"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
  return w[n];  
 }
 int counter()
 {
  int cnt=0;
  //static int cnt=0;
  //静态局部变量,第一次使用的时候分配空间,一直保留直到程序结束。static局部变量只初始化一次。
 
  return ++cnt;
 }
 
 int main()
 {
  printf("%s\n",weekday(3));
  int i;
  //auto signed int i;//自动,基本不用。
  for (i=0;i<5;i++)
   printf("%d\n",counter());
  return 0;
 }
 
 由于全局变量并不安全,所以程序里面尽量少用全局变量。
 静态全局变量和局部变量不会有垃圾数据,要么是0要么是指定的数据。
 
 time:从1970.1.1 00:00:00计算到现在的时间的秒数
 
 
 register寄存器---请求把变量放到寄存器中而不是内存中;寄存器比内存快
  寄存器是没有地址的;通过&i取地址会报错。
 #include  <stdio.h>
 
 int main()
 {
  register int i=100;//register是一个使用寄存器的请求,用不用不一定
  printf("%d\n",i);
  return 0;
  
 }
 
 volatile int n;//这个变量可能会随时被莫名其妙的修改
 一般会用在嵌入式开发中,可能由某个硬件直接来控制(如传感器)。