嗨翻C语言笔记(部分引用)

1.        编译:gcc cards.c  -o cards

另外-o表示优化级别-o2 -o3  -ofast级别依次升高,相对应的编译速度也依次变慢
运行:  ./cards(类linux中)
           cards(windows)
合并执行 gcc cards.c -o cards && ./cards
2.      *a取内容 &a取地址

字符串指针
    I. 函数传递的情况下,eg: void fortune(char msg[])
                                             {printf("%s\n",msg);             //输出字符串的内容,字符串输出时给出首地址就行不是*msg
                                              pintg("%i"),sizeof(msg));}  // 结果为指针的大小(msg[]实际为指针,只是接了个数组地址,指针本身并不能说明数组长度),不是字符串的大小,在32位系统中占4个字节,64位系统中占8个字节。因:4*8位=32,8*8位=64
但是不做这种传递的字符数组变量(不用指针),可以用sizeof取其大小的,eg:char s[]="How big is it?";char *t=s;
sizeof(s)=15;   //一个字母占一个字节          sizeof(t)=4或8
另外,传递数组时 void fortune(char msg[])变成char msg[4]之类的可以通过编译,但本身并无任何实际意义,函数中可以用msg[5]等,表示给这种标识无意义。
 II.int num
     scanf("%i",&num)
    char name[40];
    scanf("%s",name) //可用eg:%39s限制输入过多越界,程序崩溃。
    另外:scanf 和 fgets
   scanf数值字符串都行,但 不适合用于输入字符串中有空格之类的。printf字符串遇到空格可没事。
    fgets输入字符串时更适合,但必须限定长度。eg: char food[5]; fgets(food,5,stdin)//赋值给food,长度为5或者用sizeof(food),stdin ,表示标准输入来自键盘。
scanf也可以通过,[^\n]来读字符串,但是[^\n]具体什么意思(只说是表示这一行剩下的数据都给他,但是由下图,需要两次回车才能结束输入,和140页的例子我由于[^\n]忘了加\n导致只能读入第一行数据,所以有点不懂了,用时先试试吧)
 

           III, 指向字符串字面值的指针变量,不能用来修改字符串的内容。
              eg: char *cards="JQK";//不能用cards[0]等做改值操作因其储存在常量区,可在前加const,这样编译器会提示错误信息,read-only。
                但字符数组就可以, char cards[]="JQK";
一个C一行一行操作文件的例子
          #include<stdio.h>

#include<stdlib.h>
#include<string.h>
int main()
{
 char  line[80];
 FILE *in=fopen("spooky.txt","r");
 FILE *file1=fopen("ufos.csv","w");
 FILE *file2=fopen("disappearances.csv","w");
 FILE *file3=fopen("other.csv","a");//a表示追加模式,若原已有此文件,则在原有数
 //数据后继续写入,而若w模式,若原有文件,则抹掉原数据,写入新数据 
 
while(fscanf(in,"%79[^\n]\n",line)==1){
//把\n去掉就只读一行数据了。是读一行还是显示时到\n停止了 
 //while(fscanf(in,"%79s",line)==1){
      //这个也行,所以别用[^\n了] 
      if(strstr(line,"UFO"))
         fprintf(file1,"%s\n",line);
      else if(strstr(line,"Disappearance"))
            fprintf(file2,"%s\n",line);
           else 
            fprintf(file3,"%s\n",line); 
 
       }
   
 fclose(file1);
 fclose(file2);
 fclose(file3);
 return 0;
}

3.   const 和static
    由上引出了const 是常量值,以前自己总是和static静态混。另外还有一个可以定义用于多个文件的extern关键字。
把任何不想改变的对象声明为const是一种良好的习惯,还有一种这样的define N 12,注意后面别加冒号
const作用
声明常量,它不能被修改,它存放在常量区
  
  
    
    
static作用
1限制范围(此例左中只能在counter函数中访问,所以在主函数用s承接,右例中则都可以访问,这是用它声明全局变量的注意点。注意左中,虽然每次调用函数,但是count并不是每次都被重新赋值为1) 
2设定变量存储区域(静态存储区域) 
    
    
(C#中类定义为static表示不能用此类实例化,所以只能通过名字作用域引用,而函数定义为static是为了防止函数重名,重新定义,加入static后函数就定了,若再加入同名函数的不同方法则会报错。类中变量成员定义为static则为为此类建一个成员,而非为每个实例,详细见C++数据结构笔记9)
     
4. 语句本身有值
  eg:t=3,此语句本身就是值3,所以有f=t=3,得f=3;
       scanf()函数可返回成功读取的数据条数。eg: while(scanf("%f","%f","%i",&a,&b,&c)==3)做判断条件。fgets返回的是指针。

5, Ctrl+D可停止程序
6. 重定向输入输出
 I.在终端中,可用<重定向标准输入,>重定向标准输出。
eg: ./geo2json < a.csv > output.json   //运行程序geo2jsion,从a.csv中读入,输出到output.json中。
另:在类linux中看错误,输入,echo $?   另补:C代码中system(cmd);可执行命令行p400,但用此容易被注入,不安全所以有了
exec(),但此会终止当前程序,于是又引出了fork()+exec()运行子进程。p424
printf>>fprintf(stdout,"这是标准输出!")
也可以通过fprintf(stderr,"错误信息:%s\n",变量名)//把错误信息过滤,单独输出到屏幕
所以和上面的一块用就可以把正常的数据输出到文件,错误的数据在屏幕上提示,当然也可以输出到某个文件中。P124
II. 用C语言代码重定向输入输出,即重定向输入输出数据流
  #include<stdlib.h>
File *in_file=fopen("input.txt","r");//从input里读数据,我试过若是无此文件,自己代码不检查错误,编译不报错。但打开失败返回0(一般语句失败返回0或-1,)
File *out_file=fopen("output.txt","w");//r、w、a、三种模式,r是读,w是写,a是追加,w和a若原无此文件都可新建,另外,a表示追加模式,若原已有此文件,则在原有数据后继续写入,而若w模式,若原有文件,则抹掉原数据,写入新数据 
写入: fprintf(outfile,"内容 %s和 %s",“内容1”,“内容2”);//把两个内容写入到output
fscanf(in_file,"%9s",变量);
fclose(in_file);
fclose(out_file);
注意:用完必须关闭数据流。例子在P140页,不过不用再去纠结那个[^\n]\n了,直接用S吧。

7. 管道"|",一个程序的输出直接作为另一个程序的输入
(./ber|./geo)<in.csv > output.json   //实际in.csv>>ber处理>>结果给geo处理>>输出到output中.p131

8. 由P186想编C可这样借鉴C#的三层架构
同类函数全局变量声明在一个.h文件中
再用一个.c文件在其中把相应函数具体定义,并include...h
这样其他文件就可以通过include....h,使用....c中的函数,
最表层的执行放在最上层。
另外:共享函数,共享变量需eg: extern int a(头文件中比较适合,是外部变量引用)

9.P239和236 结构体和类实例一样若传递函数用,函数名(class/struct T)这种,副本改值不影响原始(按值传递),但可以这样改变初始值,eg:C结构体改变其初始值,用结构体指针指向保存数据的地址。另外,上面说的是类的实例,若整个类的初始值需要改变则涉及到类中static变量的定义,看看C++数据结构笔记9. 再另外,对应按值传递,这种指针传递,还有按引用传递,详细见C++数据结构笔记14,
eg: 
  void happy_birth(turtle *t)    //turtle是已经定义好的一个结构体的名字
{
    (*t).age=(*t).age+1;

}

(*t).age  和t->age//这两个表示方式等价

10.  unsigned int low_pass:1; //表示只占一位的int型,注意要规定用几位前面必须是unsigned,

通常多个这样的计算机会放一起,能组成一个字节就组成一个字节,节约空间
C语言默认函数类型是 Int,不是void,C++、C#也是
但是C99新标准规定主函数必须返回int,不能默认那种

11. 数组的长度是固定的,为了保存可变数量的数据,可用链表,链表插值比数组方便的多。

12. malloc()函数可用于申请堆空间,因函数内变量一般放在栈空间,结构体等数据较多时数据较大可申请堆空间,
当然用完还是必须归还,free()。

13.C中函数参数数量可变,//P344
eg: void pint(tin a, ...){}                //


14.Linux 中输入  ps -ef查看进程, windows中 taskmgr

15. 进程:P431
 文件描述符对应数据流
 进程含有正在运行的程序,还有栈和堆数据空间等
# 数据流
0 键盘    //标准输入
1 屏幕    //标准输出
2 屏幕   //标准输出
。。。。
FILE *my_file=fopen();
操作系统会遍历描述符表寻找空项,把新文件注册在其中
如果不知道描述符号fileno()返回
int a=fileno(my_file);
 dup2(4,3)复制数据流4的到3的数据流上,
dup2(fileno(my_file),3);

进程的管理
 进程内部有符号表,相互通信pipe,而进程和系统相当于雇员和老板的关系,老板给出信号,雇员可以有自己的处理方式(写自定义处理函数),也有默认的方式,有此开明也有强制措施,eg:kill -kill,当然雇员也可以给自己发出信号(raise()),让自己做什么,这是自愿做的。
eg: alarm(5); //倒计时5秒,
发出SIGALRM信号,
然后>写自定义处理函数>用sigaction包装>sigaction()注册 ,处理函数P462

16.三目运算符 return (x==1)?2:3  //真时返回2,假时返回3

17.位运算, ~a, a中所有位取反     a&b,a|b,a^b   a中的位“与”,“或”,“异或”b中的位
<<位左移(值增加)     >>位右移(值减小)
<<可用来快速将某个整型值乘以2的幂,但小心溢出

18.逗号分割表达式
    for(i=0;i<10;i++,j++)

19.  预处理指令
#define  AS 7 //程序中所有出现AS的地方被替换为7
带参数的:
#define    AS(x)  ((x)+1)   //程序中所有出现AS(x)的地方被替换为。。
带条件的
#ifdef SPANISH         //如果SPANISH这个宏存在
char *s="ang";           //就包含着段代码
#else
char *s="wngla";         //否则就包含着段代码
#endif

20. limits.h 可以查看某类型数据数值的上下限 eg: INT_MIN,INT_MAX等表示int 型最小值最大值
程序中include后直接用变量名就好。
或者c++中climits,  include<climits>// include<limits>
21. 函数指针 
  C语言中函数名也是指针变量,指向函数地址,因无函数类型,所以不能function *f这样
eg:把函数int ns(char *s)传递给函数 void find(), find应这样定义
void find(int (*match)(char*))   //因match指向函数地址所以加*号,后面()是参数的类型
 {    里面用时,match(char *s)就表示传来的函数}   //定义那样定,但是这可以用也可以不用(*match)(char *s)
eg:#include<stdio.h>
#include<windows.h>
typedef int(*lpAddFun)(int, int);//这样就是声明一个函数指针变量。前面加typedef就是定义函数指针类型。
int main()
{
lpAddFun addFun;//函数指针
addFun=(lpAddFun)GetProcAddress(hDll,"add");//addFun相当于承接了dll中的函数add
int result=addFun(2,3);
……}
调用: find(ns)也可以写成find(&ns);     函数指针是唯一可以省略*和&编译器也能识别的指针。 
另外区别返回值类型,
eg: void* lots(){}           //是返回空指针(空指针可以被任意类型的指针接受)是返回值类型,不是函数类型,P510
      

22 rand() 产生伪随机数
  若要增加随机性,可用srand初始化随机种子,但是这里是想记一小技巧,比如说想产生0到9的数,而rand产生的是0到RAND_MAX之间的,我可以这样。int a=rand()%10;  巧用取余符号,将随机数限定。同理0到100的可以%100,等等,方便快捷。
但真正规范的是int  j=(int)(n*rand()/(RAND_MAX+1.0))产生一个0到n之间的随机数,这是采用一个0~1的数乘以n的思想,分母加1,取不到n了。
若1到n之类,则可采用平移的思想 int j=1+ (int)(n*rand()/(RAND_MAX+1.0))

rand()和srand()的用法
首先我们要对rand()&srand有个总体的看法:srand初始化随机种子,rand产生随机数,下面将详细说明。

rand(产生随机数)
表头文件: #include<stdlib.h>

定义函数 :int rand(void)

函数说明 :
因为rand的内部实现是用线性同余法做的,他不是真的随机数,只不过是因为其周期特别长,所以有一定的范围里可看成是随机的,rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。rand ()产生的是假随机数字,每次执行时是相同的。若要不同,以不同的值来初始化它.初始化的函数就是srand()。

返回值:
返回0至RAND_MAX之间的随机整数值,RAND_MAX的范围最少是在32767之间(int),即双字节(16位数)。若用unsigned int 双字节是65535,四字节是4294967295的整数范围。
0~RAND_MAX每个数字被选中的机率是相同的。

范例:
/* 产生介于1 到10 间的随机数值,此范例未设随机数种子,完整的随机数产生请参考
srand()*/
#include<stdlib.h>
main()
{
int i,j;
for(i=0;i<10;i++)
{
j=1+(int)(10.0*rand()/(RAND_MAX+1.0));
printf("%d ",j);
}
}
   执行:

9 4 8 8 10 2 4 8 3 6
9 4 8 8 10 2 4 8 3 6 //再次执行仍然产生相同的随机数

srand(设置随机数种子)
表头文件:#include<stdlib.h>

定义函数:void srand (unsigned int seed);

函数说明:
srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,通常可以利用geypid()或time(0)的返回值来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。

范例
/* 产生介于1 到10 间的随机数值,此范例与执行结果可与rand()参照*/
#include<time.h>
#include<stdlib.h>
main()
{
int i,j;
srand((int)time(0));
for(i=0;i<10;i++)
{
j=1+(int)(10.0*rand()/(RAND_MAX+1.0));
printf(" %d ",j);
}
}
     执行:与rand范例比较
5 8 8 8 10 2 10 8 9 9
2 9 7 4 10 3 2 10 8 7
又或:
用"int x = rand() % 100;"来生成 0 到 100 之间的随机数这种方法是不或取的,比较好的做法是: j=(int)(n*rand()/(RAND_MAX+1.0))产生一个0到n之间的随机数
int main(void)
{
   int i;
   time_t t;
   srand((unsigned) time(&t));
   printf("Ten random numbers from 0 to 99\n\n");
   for(i=0; i<10; i++)
       printf("%d\n", rand() % 100);
   return 0;
}
除以上所说的之外,补充一点就是srand这个函数一定要放在循环外面或者是循环调用的外面,否则的话得到的是相同的数字。

MSDN中的例子。
// crt_rand.c
// This program seeds the random-number generator
// with the time, then displays 10 random integers.
//
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main( void )
{
   int i;
  
   // Seed the random-number generator with current time so that
   // the numbers will be different every time we run.
   //
   srand( (unsigned)time( NULL ) );
   // Display 10 numbers.
   for( i = 0;   i < 10;i++ )
      printf( "  %6d\n", rand() );
  printf("\n");
  // Usually, you will want to generate a number in a specific range,
  // such as 0 to 100, like this:
  {
     int RANGE_MIN = 0;
     int RANGE_MAX = 100;
     for (i = 0;    i < 10; i++ )
      {
         int rand100 = (((double) rand() /
                        (double) RAND_MAX) * RANGE_MAX + RANGE_MIN);
         printf( "  %6d\n", rand100);
      }
  }
总结:
我们知道rand()函数可以用来产生随机数,但是这不是真真意义上的随机数,是一个伪随机数,是根据一个数,我们可以称它为种了,为基准以某个递推公式推算出来的一系数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非你破坏了系统,为了改变这个种子的值,C提供了 srand()函数,它的原形是void srand( int a) 功能是
初始化随机产生器既rand()函数的初始值,即使把种子的值改成a; 从这你可以看到通过sand()函数,我们是可以产生可以预见的随机序列,
那我们如何才能产生不可预见的随机序列呢?我们可能常常需要这样的随机序列,是吧。利用srand((unsign)(time(NULL))是一种方法,因为每一次运行程序的时间是不同的,对了,你知道time() 函数的功能是返回从1970/01/01到现在的秒数的吧,可能这个起始时间不正确,你查一下对不对吧,C还提供了另一个更方便的函数, randomize()
原形是void randomize(),功能是用来始初rand() 的种子的初始值,而且该值是不确定的,它相当于srand((unsign)(time(NULL)) 不过应注意的是randomize()的功能要通过time来实现所以在调用它时头文件要包含time.h罢了
23 .数组名和指针
定义数组a,指针pa
数组做右值时转换成指向首元素的指针,但做左值仍然表示整个数组的存储空间,而不是首元素的存储空间。所以像a++,a=pa+1,都是错误的(第一个a++容易与指针自加混,指针自加是可以的,加1相当于加上自身指向类型的长度),而&a是合法的
另:a[2]=*(a+2)所以数组从零开始



 

 

 

 

 

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值