Skip to content

android-nuc/17-C-Train

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

17 C Train

C training for 17 fresh man

10.14 大一c语言培训(一)

时间: 8:00 - 12:00

  • 初始代码:(你好,世界!打开新世界的大门!)
    #include<stdio.h>
    int main()
    {
        printf("Hello World\n");
        return 0;
    }
  • 代码编写规范

    • 标识符命名规范:标识符只能由字母(AZ, az)、数字(0~9)和下划线_组成,并且第一个字符必须是字母或下划线
  • 顺序结构:

    • 基本数据类型
      • char, short, int, long int, float, double
      • 基本数据类型所占直接数与三个方面因素有关:
      • CPU位宽(即你的CPU是多少位的)
      • 操作系统位宽(笼统说就是操作系统位数,操作系统位宽取决于CPU位宽)
      • 编译器类型和版本
数据类型 所占字节数 取值范围
short 2 -3276832767,即 -215(215-1)
unsigned short 2 065535,即 0(216-1)
int 4 -21474836482147483647,即 -231(231-1)
unsigned int 4 04294967295,即0(232-1)
long 4 -21474836482147483647,即 -231(231-1)
unsigned long 4 04294967295,即0(232-1)
  • 无符号(unsigned)与有符号(signed)的区别(int为例子) - 一般的,在你未定义是无符号(unsigned)时,编译器默认的是有符号型(signed) - 两者的区别与数值在内存中的存储有关。(原码、反码和补码) - 举例子为:
```
unsigned int a;
int b = -1;
a = b;
printf("a=%u",a);
```   
  • 关系运算符
关系运算符 含义
< 小于
<= 小于或等于

| 大于 = | 大于或等于 == | 等于 != | 不等于

  • 逻辑运算符
运算符 说明
&& 与运算,双目,对应数学中的“且”
``
! 非运算,单目,对应数学中的“非”
  • 转义字符
    • \n, \t,\0
  • i++与++i的区别
    • ++ 在前面叫做前自增(例如 ++a)。前自增先进行自增操作,再进行其他操作。
    • ++ 在后面叫做后自增(例如 a++)。后自增先进行其他操作,再进行自增操作。
    int a,b,c,d;
    a = 2;
    b = 3;
    c=a++;
    d=++b;
    
    • 自减(--)也一样,有前自减和后自减之分。
    • 自增自减完成后,会用新值替换旧值,并将新值保存在当前变量中。自增自减只能针对变量,不能针对数字
  • 输入输出
    • getchar()putchar() : 字符的输入输出
    • gets()puts() : 字符串的输入输出
    • scanf()printf() : 可以输入输出各种类型的数据
数据类型 输出格式
int %d
long int %ld
char %c
float %f
double %lf
八进制整数 %o
指针 %p
无符号十进制整数 %u
  • 常量
    • const int max = 100;
    • const与define所申明的是有区别的。const只是将变量申明成只读的。
    • #define MAX 100 (宏定义)
    • 数值宏常量:例如: #define a 10 (注意,中间是有空格的)
    • #define替换:在调用宏时,首先对其参数检查,如果参数又包含#define定义的符号,则会被替换。
    • #undef用于移除一个宏定义
  • 变量 - int a = 1;

  • 选择结构:
    • if-else语句
        if(判断条件)
        {
            //真 to-do
        }
        else
        {
            //假 to-do
        }
      
    • switch语句
        switch(表达式)
        {
            case 数字/单个字符:
                // to-do
                break;
            case 数字/单个字符:
                // to-do
                break;
            default:
                // to-do
                break;
        }
      
    • 三目运算符 : 判断条件 ? 真 : 假;
题目:

请编写一个程序,它的功能是:计算三位整数中的各位数字之和。输入一个三位整数,函数返回各位数字之和。

  • 循环结构:
    • while循环
        while(表达式)
        {
            // to-do
        }
      
    • do-while循环
      do
      {
        // to-do
      }while(表达式);  //do-while后面要加分号
      
    • for循环
        for(循环变量的初始化; 循环判断条件; 循环递增条件)
        {
            // to-do
        }
      
      • 什么时候用whlie或do-while:
        1. 循环次数不确定
        2. 循环的改变不是递增或递减
    • continue和break的区别
      • continue 结束本次 循环,进行下一次循环
        break 终止 循环不再进行

题目:

请编写一个程序,它的功能是:统计素数的个数。输入一个自然数n(n在100到10000之间),函数返回100到n之间(包括n)素数的个数。

  • 代码块:
    • 代码块就是位于一对大括号之内的可选的声明可语句列表。简单的来讲,一对大括号内的就是一个代码块。

  • 数组:
    • 一维数组
    • 定义与初始化:数组在声明的时候必须指定其大小,如:a[10],因为数组内存的分配是在编译期间进行的
    • 内存布局与边界:一维数组在内存中是连续存储的,其中下标从0开始
    • 不完整初始化:例如:int b[5]={1};,则默认第一个元素是1,其余的都是0。
    • 字符串数组的初始化:例如:char c[5]={'h','e','l','l','o'};char c[]="hello";两者是不同的,字符串默认以'\0'结尾
    • 二维数组
    • 存储顺序:二维数组可以看成是一个一维数组,只不过这个一维数组里面的元素又是一个一维数组。所以还是以线性存储的方式存储在内存中。
    • 数组名:二维数组的数组名是一个数组指针,它指向第0行。例如:int a[3][4];。其中,a[i]是每行首元素的地址。&a[i]是一个数组指针,代表每行的地址。
    • 例如:二维数组A[m][n]可以视为由m个行向量组成的向量,或者是由n个列向量组成的向量。
    • 访问形式:二维数组的访问形式有下标和解引用两种形式,不过即使是下标访问也是先被解析成解引用的形式再访问的。例如:要访问第三行第四列,则有,1、a[3][4];2、*(*(a+3)+4)

题目:

统计数列中符合条件数据的个数,输入10个整数构成一个数列,返回数列中正整数的个数。


题目:

将一个数组中的值按照逆序重新存放。原数组(包含5个元素),逆序之后进行输出。例如:1 2 3 4 5 输出为:5 4 3 2 1


  • 函数:

    • 定义:函数的定义就是函数体的体现,函数体就是一个代码块,它在函数被调用时执行。函数定义语法:类型 函数名(形参) {函数体}。如:int query(char a[],int x) {}

      • 第一种使用函数方式:
      void main()
      {
        void output();
        int a;
        ...
      }
      
      void output()
      {
        printf("HelloWorld!\n");
      }
      
      • 第二种使用函数方式:
      void output()
      {
        printf("HelloWorld!\n");
      }
      
      void main()
      {
        void output();
        int a;
        ...
      }
      
      
    • 参数:

    • 参数传递:传值与传地址。要想在变量所声明的代码块外部改变变量,则应该进行“传地址”。

    • 形参与实参

    void add(int a,int b)
    {
      ...
    }
    void main()
    {
      int c,d;
      c = 1;
      d = 2;
      add(c,d);
    }
    
    • 递归的使用
    • 递归函数就是直接或间接的调用自身函数。递归必须要有递归的终止条件。
    • 下面是一个5的阶乘的递归的例子:
   int factorial(int n)
 {
     int result;
     if (n<0)                                         
     {
         printf("输入错误!\n");
         return 0;
     }
     else if (n==0 || n==1)
     {
        result = 1;  					                          
     }
     else
     {
        result = factorial(n-1) * n;            
     }
     return result;
 }
 int main()
 {
     int n = 5;                                             
     printf("%d的阶乘=%d",n,factorial(n));
     return 0;
 }

10.15 大一c语言培训(二)

时间: 8:00 - 12:00

指针基本概念

"&"和"*"

  • 取地址和指针运算符:
int i = 5;
printf("%#X",&i);//打印出地址   
scanf("%d",&i);//传入一个地址
//scanf的函数原型
int scanf(const char * restrict format,...);

百度百科

函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。

  • 而 " ** "是指针运算符,其实可以看作是一个取值运算符,它可以得到该指针所指向的内容,可以看作* *(变量名) => 把变量当作地址,到相应的地址取值。

  • 思考:

int i = 5;
printf("%d",*(&i));

什么是指针变量?

指针变量就是来存储地址的变量,任何变量都有地址,这个地址是随机分配的:

int i = 10;

这时的i就有自己的地址,我们可以用去地址符来得到它的地址。

如何定义指针变量?

  • 声明方式:

数据类型 *指针变量名

int *pointer;
char *name;

这样就定义好了两个指针变量,int和char表示该这两个指针变量指向的数据类型,*表示这是指针变量。

  • 初始化:
int i = 5;
int *p;
p = &i;

值得注意的是,p定义成为一个指针变量,我们这里把变量i的地址赋值给它,此时p就指向了i,因为p需要的是一个地址,所以i之前必须加上取地址符**。
赋值的时候不需要再加上*,可以像使用普通变量的方式来使用指针变量,也就是说在定义指针变量时要加上**来表示这是一个指针变量,而在给指针变量赋值的时候不能带*。


题目:

使用指针交换两个整数变量的值,并写成函数形式,即实现void swap(int *a, int *b)函数

指针的运算

指针就是地址,地址在内存中也是以数的形式存在,所以指针也能做加法,减法,比较等运算,我们这里只提一下指针的加法,后面会用到。

int a = 5;
int *i = &a;
printf("%#X\n",i);
i++;
printf("%#X\n",i);
return 0;

从输出中可以看到,它的地址增加了4,4正好是int类型在内存中的长度。

所以指针的加减法并不是简单的加一减一,它和数据类型的长度有关。

指针与数组的关系

c语言定义数组的时候需要数组名和数组长度,数组名其实可以认为是一个指针,它指向数组的首元素,c语言中,把数组首元素的地址称为数组的首地址。所以数组名可以认为是数组的首地址,但是有些情况不等价。

char a[] = "barack";
printf("%#X\n",a);

输出的是地址。

  • 使用指针来遍历数组

我们先来写个例子演示一下

int main()
{
	int array[] = {10,2,34,54,6};
	int length = sizeof(array) / sizeof(int);
	int i;
	for(i = 0; i < length; i++)
	{
		//等价于array[i];
		printf("%d ",*(array + i));
	}
	return 0;
}

我们看一下这里的*(array + i),array就是数组名,看作是数组的首地址,指向数组的首元素,array + i指向数组的第i个元素,再加上指针运算符就得到了该元素的值。它等价于array[i]。array每次加一的时候,它自己的值都会增加sizeof(int),加i的时候就增加i*sizeof(int)。


题目:

请编写一个函数fun(),它的功能是:利用指针计算整数数列中各元素的和。输入包含5个数的整数数列,利用指针返回5个数的和.

字符串指针

先来复习一下字符串,c语言中,没有专门的字符串类型,使用字符数组来实现字符串:

它可以这样定义:

char string[5] = {'a','b','c','d','e'};

也可以这样定义:

char string[] = "HelloWorld";

c语言字符串都是以'\0'作为结束表示,上面定义的两个字符串编译器已经自动加上了。

输出的时候可以使用puts()或printf()函数,这两个函数在输出的时候都会逐个扫描字符,直到遇见'\0'才结束输出。

  • 字符串指针

字符数组说到底还是一个数组,所以上面提到的指针于数组的关系同样适用,下面以指针的方式输出字符串:

char str[] = "baoqianyue";
char *pstr = str;
int len = strlen(str),i;
for(i = 0; i < len; i++)
{
	printf("%c",*(pstr + i));
}
printf("\n");
for(i = 0; i < len; i++)
{
	printf("%c",str[i]);
}
printf("\n");
for(i = 0; i < len; i++)
{
	printf("%c",*(str + i));
}
printf("\n");

所以我们有了下面一种定义字符串的方式,在后面会比较常用:

char *str = "baoqianyue";
//或者
char *str;
str = "baoqianyue";
//这样str就指向了第0个字符。

如何输出它:

printf("%s",str);

for(i = 0; i < len; i++)
{
		printf("%c",*(str + i));
		printf("%c",str[i]);
}

题目:

实现字符串长度计算函数strlen()

指针数组

这个可以从定语的角度得知一个数组中的元素都是指针的话,就称它为指针数组,指针数组的定义方式为:

datatype *array[length];

因为[]*的优先级高,所以也可以看成是**(array[length]),这样括号里面array[length]是一个长度为length的数组,括号外面说明数组的元素类型为datatype*的指针类型。

写个例子表示一下指针数组:

int a = 10, b = 2, c = 5;
int *array[3] = {&a,&b,&c};
printf("%d %d %d\n",*array[0],*array[1],*array[2]);
return 0;

可以输出a,b,c的值,因为数组array里面的元素都是指针,所以我们在对他初始化的时候以abc的地址来进行,和普通数组的初始化很相似。

在输出的时候,我们先取数组中的元素,比如array[1],这个元素是个地址,我们要拿到该指针指向的值就要对该地址加上一个指针运算符*。

数组指针

定义方式:

datatype (*p)[length];

如果一个指针指向了数组,就称它为数组指针。我们先复习一下二维数组的概念:

int a[4][3] = {{0,2,3},{1,5,6},{2,3,4},{7,8,9}};

它就好像是一个矩阵:

0 2 3
1 5 6
2 3 4
7 8 9

我们可以把二维数组分解成多个一维数组,这个就可以分为a[0],a[1],a[2],a[4]四个一维数组,a[0]就是一维数组,a[0]包括a[0][0],a[0][1],a[0][2],a[0][3]四个元素,前面说过数组名可以看作是一个地址,这里的a就是那四个一维数组名,所以下面可以定义一个数组指针并赋值为a;

我们定义个数组指针:

int (*p)[4] = a;

括号中的*代表p是一个指针,[4]表示这个指针指向数组,数组的类型是int [4],就是上面提到的a[0],a[1],a[2],a[3]这四个数组的类型。

其实在实际的编译过程中,数组名a也会被转换为和p等价的指针。

使用数组指针来遍历一下二维数组:

p 指向数组a开头,就是指向第0行元素,p+1 代表前进一行,指向第1行元素。

*(p+1)表示取地址上的数据,这里p+1地址指向第一行数据,就是a[1],注意是一行数据,是多个数据。

*(p+1)+1表示第一行的第一个数据的地址。

((p+1)+1)表示第一行的第一个数据的值。

有以下等价关系:

a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)     

结构体

  • 结构体的定义

前面说过的数组是一种存放数据的结构,但是数组的特点是数组内的元素的类型都相同,如果我们现在要一组类型不同的数据,比如我们要存储班级同学的姓名年龄等信息,姓名是字符串,年龄是整数,这个时候使用数组来存储是实现不了的,就要用到结构体。

在c语言中,可以用结构体存放一组不同类型的数据:

struct student{
	char *name;
	int age;
	int number;
};

这个结构体名是student,它内部有三个成员,分别为姓名,年龄,学号。定义形式与普通变量定义的方式一样,只不过不能立即初始化。

**注意大括号后面要加上分号,这是一条完整的语句。 **

  • 结构体变量

结构体也是一种数据类型,在某种意义上与int,char这些基本数据类型是同级的,所以定义变量的方式是一样的。

struct student stu1,stu2;

注意关键字struct不能少。

这样就定义好了两个结构体变量,可以看作是两个学生,上面定义的结构体相当于一个模板,现在这两个结构体变量就仿照着我们定义的结构体在内部构建自己的属性,也就是说定义出来的两个变量也具有和上面结构体一样的姓名,年龄,学号三个属性。

可以在定义完结构体之后立马定义变量:

struct student{
	char *name;
	int age;
	int number;
} stu1,stu2;
  • 结构体成员的读取和赋值

结构体成员的获取形式为:

结构体变量名.成员名;

写个例子来表示一下获取和赋值:

int main()
{
	struct student{
	char *name;
	int age;
	int number;
} stu;
	stu.name = "baoqianyue";
	stu.age = 19;
	stu.number = 11111;

	printf("%s的年龄是: %d\n%s的学号是:%d\n",stu.name,stu.age,stu.name,stu.number);
	return 0;
}

还可以直接整体赋值:

int main()
{
	struct student{
	char *name;
	int age;
	int number;
} stu = {"baoqianyue",19,11111};
  • 结构体数组

结构体数组,是指数组中的每个元素都是结构体。

定义并初始化结构体数组:

struct student{
	char *name;
	int age;
	int number;
} class[2] = {
	{"baoqianyue",19,11111},
	{"tanzhuo",19,11112}
};

获取结构体数组元素成员:

class[0].name;
  • 结构体指针

就是指向结构体的指针,定义形式:

struct student{
		char *name;
		int age;
		int number;
} stu1 = {"baoqianyue",19,11111};
//结构体指针
struct stu *pstu = &stu1;

也可以在定义结构体的同时定义结构体指针

struct student{
		char *name;
		int age;
		int number;
} stu1 = {"baoqianyue",19,11111}, *pstu = &stu1;     

结构体变量名和数组名不一样,不能看作地址

struct student *pstu = stu1;

这样是错误的。

  • 使用结构体指针获取结构体成员

两种形式:

(*stu1).name;

.的优先级高于*,所以要先用指针运算符得到指针stu1指向的结构体,然后再获取成员。

stu1 -> name;

这种写法用到了一个新的运算符,通过这个箭头,可以直接获取到结构体成员。

举例演示:

struct student{
		char *name;
		int age;
		int number;
	} stu = {"barack",19,11111};

	struct student *pstu = &stu;

	printf("%s的年龄:%d\n%s的学号:%d\n",pstu -> name,pstu -> age,pstu -> name,pstu -> number);

关键字typedef

c语言允许为一个数据类型起一个新的别名,就像你给别人其外号一样。

  • 定义方式:
typedef oldname newname;
  • 举个例子:
typedef int data;
typedef struct student{
	char *name;
	data age;
	data number;
} STUDENT

这里data就是int的别名,而STUDENT就是struct student的别名。

STUDENT stu1,stu2;

相当于

struct student stu1,stu2;

Releases

No releases published

Packages

No packages published