首页 > 代码库 > C语言学习笔记

C语言学习笔记

————————————————————————————————————————————

getchar /putchar 输入输出

语法:

c = getchar(); // 获取控制台输入

putchar(c); //输出

————————————————————————————————————————————

EOF,End Of File,文件尾标志。 从数值上来看,就是整数-1

linux:在输入回车换行后的空行位置,按 ctrl+d (先按ctrl键,不放,再按d键)

windows:在输入回车换行后的空行位置,按 ctrl+z,再回车确认

————————————————————————————————————————————

参数传值调用

在C语言中,被调函数不能直接修改主调函数中变量的值,而只能修改函数私有的临时副本的值

必要时,也能够修改主调函数中的变量。需要向被调用函数提供待设置值的变量的地址(指针)。被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量。

如果是数组参数,当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址,并不复制数组元素的本身。在被调函数中,可以通过数组下标访问或修改数组元素的值。

技术分享

————————————————————————————————————————————

变量

  1. 自动变量(局部变量) //在一个函数开头或段开头处说明的变量

    作用域:

    仅在定义它的函数内;

    初始化:

    不自动赋初值,使用前需要赋值;

    值的保持:

    随函数的引用而存在和消失,不保持值;

       

  2. 外部变量(全程变量) //在函数外部定义的变量

    优势:

    解决函数单独编译的协调;与变量初始化有关;外部变量的值是永久的;解决数据共享;

    作用域:

    整个程序;

    初始化:

    0

    值的保持:

    永久保持

    特点:

    1. c程序可以分别放在几个文件上,每个文件可以作为一个编译单位分别进行编译。外部变量只需在某个文件上定义一次,其它文件若要引用此变量时,应用extern加以说明(外部变量定义时不必加extern关键字)
    2. 在同一文件中,若前面的函数要引用后面定义的外部(在函数之外)变量时,在函数里加extern加以说明。

    举例:

    技术分享

       

  3. 静态变量 - 内部 //在局部变量前加上static

    作用域:

    仅在定义它的函数内;

    初始化:

    0

    值的保持:

    当函数执行完,返回调用点时,该变量并不撤销,再次调用时,其值将继续存在;

    特点:

    采用静态存贮分配(由编译程序在编译时分配,而一般的自动变量和函数形参均采用动态存贮分配,即在运行时分配空间);

       

  4. 静态变量 - 外部 //在函数外部定义的变量前加static

    优势:

    可以实现数据隐藏

    作用域:

    定义它的文件(该文件的私有变量),其他文件上的函数不允许访问;

    初始化:

    0

  5. 寄存器变量 //只有自动(局部)变量和函数参数才能进一步指定为寄存器存贮类

    特点:

    1. 使用register变量可以提高存取速度,但寄存器变量的数目依赖于具体机器,声明多了也只有前几个有效。
    2. 只限于int,char,short ,unsigned和指针类型用寄存类。
    3. 不能对register变量取地址(即&操作)

————————————————————————————————————————————

常量

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

enum 枚举常量

不同枚举中的名字必须互不相同,同一枚举中不同的名字可以是用相同的值;相对#define来说,优势在于常量值可以自动生成

举例:

  1. enum week {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};

    /*

    week是新的数据类型,源于int,此时:

    MONDAY = 0

    TUESDAY = 1

    WEDNESDAY = 2

    THURSDAY = 3

    FRIDAY = 4

    SATURDAY = 5

    SUNDAY = 6

    */

  2. enum week {MONDAY = 1, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};

    /*

    MONDAY = 1

    TUESDAY = 2

    WEDNESDAY = 3

    THURSDAY = 4

    FRIDAY = 5

    SATURDAY = 6

    SUNDAY = 7

    */

  3. enum escapes {BELL = ‘\a‘, BACKSPACE = ‘\b‘, TAB = ‘\t‘, NEWLINE = ‘n‘, VTAB = ‘\v‘, RETURN = ‘\r‘};

    /*

    BELL = ‘\a‘,

    BACKSPACE = ‘\b‘,

    TAB = ‘\t‘,

    NEWLINE = ‘n‘,

    VTAB = ‘\v‘,

    RETURN = ‘\r‘

    */

————————————————————————————————————————————

关键字

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

const //限定符,通过 const 对变量进行限定后,无法修改其值,数组同理。

技术分享

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

register //请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。

  1. register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。
  2. 不能通过 & 来取地址

————————————————————————————————————————————

数组

// 调用数组a[i]的值时也可以写成 *(a+i) 的形式,编译的过程实际上先将其转换成 *(a+i) 再求值。所以 a[i] 与 *(a+i) 是等价的,一个通过数组和下表实现的表达式可以通过指针可偏移量实现。注意:指针是一个变量, p=a或p++是合法的,但数组名不是变量,不可以执行上面的操作。

————————————————————————————————————————————

指针

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

地址运算符 & 只能应用于内存中的对象(变量与数组元素)。它不能作用于表达式/常量/register类型的变量。

指针只能指向某种特定类型的对象(例外情况:指向void类型的指针可以存放指向任何类型的指针,但它不能间接引用其自身)。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

指针传递

语法:

swap(a,b);

void swap(int *p,int *q)

特点:

  1. 调用方法传参,仅改变指针p的地址 &p ;
  2. *p=12赋值,仅改变内存块的值 *p ;
  3. p=&a指向变量a,仅改变内存块位置 p ;
  4. 调用方法结束,p的地址和内存块都不变,但如果在调用过程中内存块被修改,则值改变 ;

举例:

#include <stdio.h>

void pointer(int *p)

{

printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);

//Line 2 指针p传入方法pointer中:在新的方法中生成了一个p的拷贝p1,新的地址 6356752,但值和指向的内存块数据没变

*p = 12;

printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);

//Line 3 p1改变了其所指向的内存块的值为12,内存块 00F0FF2C 的值变成了22

int a = 11;

p = &a;

printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);

//Line 4 p1的值指向a,即p1指向a 内存块 0060FEFC,此时p1p分别指向不同的内存块了,不会互相影响

}

int main()

{

int b = 22;

int *p = &b;

printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);

//Line 1 *p指向b的地址:获得了p(内存块数据)&p(地址)*p(值)

pointer(p);

printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);

//Line 5 方法结束,调用方法结束后,p地址仍是调用前的地址,地址和值没变(改变的仅仅是p的拷贝p1),但是p所指向的内存块数据被p1所改变了,故*p12

}

输出:

技术分享

   

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

其他传递方式(来源参考他人)

  1. 传值方式 //和函数的值传递不是一个概念

    语法:

    swap(&a , &b);

    void swap(int *a , int *b)

    // 传入的是变量a的地址 &a,函数接收到的是传入地址的值

    // 使用指针方式修改指向内存块的值,传入的是 a、b变量地址的拷贝。

    注意:函数的值传递的方式是

    swap(a , b);

    void swap(int a , int b)

       

  2. 传引用方式

    语法:

    swap(a,b);

    void swap(int &p,int &q)

    // 使用引用方式,传入的是变量a、b,而不是拷贝,地址不变。

   

举例:

#include <stdio.h>

void swap(int *a , int *b)

//方法一:传值方式

{

printf("\n\n /*** Method 1 ***/");

printf("\n &a addr : %d , &b addr: %d", &a, &b);

printf("\n a memory : %d , b memory: %d", a, b);

printf("\n *a : %d , *b : %d", *a, *b);

int temp = *a;

*a = *b;

*b = temp;

}

void swap(int &a , int &b)

//方法二:传引用方式

{

printf("\n\n /*** Method 2 ***/");

printf("\n &a addr : %d , &b addr: %d", &a , &b);

int temp = a;

a = b;

b = temp;

}

int main(int argc, char const *argv[])

{

int a = 3 , b = 5;

printf(" &a addr : %d , &b addr: %d", &a , &b);

printf("\n a : %d , b : %d", a , b);

swap(&a , &b); //方法一

printf("\n &a addr : %d , &b addr: %d", &a, &b);

printf("\n a : %d , b : %d", a , b);

swap(a , b); //方法二

printf("\n a : %d , b : %d\n", a , b);

return 0;

}

输出:

技术分享

   

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

*ip自增

语法:

  1. *ip += 1;
  2. ++*ip;
  3. (*ip)++;

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

指针间赋值

语法: iq = ip;

/* 如果iq和ip同样是指向整型的指针,则将ip中的值copy到iq中,iq也将指向ip所指向的对象 */

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

参数指针

语法:double atof(char *);

/* atof的参数时一个指向char类型的指针*/

————————————————————————————————————————————

数组指针和指针数组

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

数组指针 //指向数组的指针

定义:int (*a)[4] //表示指向数组a的指针

举例:

#include <stdio.h>

int main(int argc, char const *argv[])

{

int a[10];

int *p, i;

for (i = 0; i < 10; ++i)

{

scanf("%d", &a[i]);

}

for (p = &a[0]; p < (a + 10); p++)

//p+i = a+i = &a[i] 这三者意思相同,所以循环结构使用 p < (a + 10),等价于 i < 10

{

printf("%d ", *p);

}

return 0;

}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

指针数组 //用于存储指针的数组

定义:int* a[4]; //表示a中的元素都为int型指针

————————————————————————————————————————————

预处理器

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

文件包含 #include "文件名" 或 #include <文件名>

  1. 用" "引起来,则在源文件所在的位置查找该文件;
  2. 如果在该位置没有找到文件或用< >括起来的,则根据相应的规则查找该文件,这个规则同具体的实现有关

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

两个预处理语句 #ifdef 和 #ifndef

用法:

#ifndef HDR

#define HDR

/* hdr.h文件的内容在这里*/

#endif

   

/* 用来测试某个名字是否已经定义,等价于 */

#if !defined(HDR)

#define HDR

#endif

举例:

/* 测试SYSTEM变量后确定包含某个文件 */

#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

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

宏替换

  1. #define 名字 替换文本

    用法:较长的替换文本可以在待续的行末尾加上反斜杠符

    举例:

    1. 例:#define forever for(;;)

      /* 该句为无限循环定义了一个新名字forever */

    2. 例:#define max(A,B) ((A)>(B)?(A):(B))

      /* 形参A和B每次出现都将被替换成对应的实际参数,不过要适当使用括号来规范计算次序的正确性 */

    3. 例:#define dprint(expr) printf(#expr " = %g\n",expr)

      /* 当使用时dprintf(x/y);时被扩展为 printf("x/y" "= %g\n",x/y); */

    4. 例:#define paste(front,back) front ## back

      /*预处理器运算符## 为宏定义提供了连接实际参数的手段*/

      /* 在调用paste(name,1)时,将建立记好 name1*/

  2. #undef getchar

    int getchar(void){...}

    用法:通过#undef 取消名字的宏定义,这样可以保证后续的调用是函数调用,而不是宏调用

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

C语言学习笔记