C语言-初阶笔记2

初阶6:函数

  1. 函数声明 void func();
返回类型 名称 (参数);
  1. 函数指针:void (*pfunc)();
类型 (*函数名) (参数)
  1. 赋值:pfunc = &func; 或者简写为pfunc=func;
  2. 函数名是地址,是执行函数代码的起始位置
  3. 调用1解引用 (*pfunc)();//前面必须加括号,因为后面括号优先级高,要把*和pfunc打包成整体才是函数指针解引用
    调用2pfunc();
  4. 函数指针可以作为函数参数,这叫回调函数
  5. 指针函数void* func(){};//返回值是一个指针类型
    示例:
#include<stdio.h>

void func();

int main(){
    printf("%p %p\n",func,&func);
    func();
    int n;
    int* p;
    int** pp;

    void(*pfunc)();//函数指针
    pfunc = &func;//赋值 pfunc=func
    (*pfunc)();//pfunc() 
    //解引用 解出来也是func的地址
}

void func(){
    printf("hello world\n");
}

重要示例!!!

#include<stdio.h>

int add(int a,int b){
    return a+b;
}
int subtract(int a,int b){
    return a-b;
}
int multipy(int a,int b){
    return a*b;
}
int divide(int a,int b){
    return a/b;
}

//int(*pfunc)(int,int)函数指针作为参数,便于调用样式统一的add,subtract,multipy,divide作为回调函数
int operation(int arr[],int n,int(*pfunc)(int,int)){
   int* p=arr;
   int res = *p;
   while(++p<arr+n){
       res = (*pfunc)(res,*p);//也可以写成res = pfunc(res,*p);
   }
   return res;
}

int print_array(int arr[],int n){
    /* for(int i=0;i<n;i++){
         printf("%d ",arr[i]);
     }
     printf("\n");*/
    int* p =arr;
    while(p<arr+n){
      printf("%d ",*p++);
    }
    printf("\n");
}

int main(){
    int n;
    scanf("%d",&n);
    int arr[n];
    for(int i=0;i<n;i++){
       scanf("%d",arr+i);
    }

    //函数指针数组,把统一样式的函数放到这个数组中,方便遍历调用
    int (*opt[])(int,int) = {add,subtract,multipy,divide};
    for(int i=0;i<4;i++){
       printf("%d\n",operation(arr,n,opt[i]));
    }
    print_array(arr,n);
}

示例(整型比较):

#include<stdio.h>
#include<stdlib.h>
void print_array(int arr[],int n){
    /* for(int i=0;i<n;i++){
         printf("%d ",arr[i]);
     }
     printf("\n");*/
    int* p =arr;
    while(p<arr+n){
      printf("%d ",*p++);
    }
    printf("\n");
}

int cmp(const void* p,const void* q){
    return *(int*)p-*(int*)q;//如果*p>*q为正值,改变顺序,从而是升
序,如果*p<*q返回为负值,是升序
 /* int* pn = (int*)p;
    int* qm = (int*)q;
    int n = *pn;
    int m = *qm;
    if(m == n){
       return 0;
    }else if(n<m){
       return 1;//正数就行
    }else{
       return -1;//负数就行
    }*/
}

int main(){
    int n;
    scanf("%d",&n);
    int arr[n];
    for(int i=0;i<n;i++){
       scanf("%d",arr+i);
    }

    //qsort(void* base,size_t nmemb,size_t size,int (*cmper)(const void*,const void *);
    //qsort(数组名,数组大小,数组元素类型大小,一个上面形式的函数指针:可以传这个样式的比较函数,满足条件时返回负值,不满足条件时传正值并改变元素顺序)
    qsort(arr,n,sizeof(int),cmp);
    print_array(arr,n);
}

示例(浮点型比较):

#include<stdio.h>
#include<stdlib.h>
void print_array(float arr[],int n){
    float* p =arr;
    while(p<arr+n){
      printf("%f ",*p++);
    }
    printf("\n");
}

int cmp(const void* p,const void* q){
    return *(float*)p>*(float*)q?1:-1;//如果*p>*q为正值,改变顺序>,从而是升序,如果*p<*q返回为负值,是升序
}

int main(){
    int n;
    scanf("%d",&n);
    float arr[n];
    for(int i=0;i<n;i++){
       scanf("%f",arr+i);
    }

    print_array(arr,n);
    //qsort(void* base,size_t nmemb,size_t size,int (*cmper)(const void*,const void *);
    //qsort(数组名,数组大小,数组元素类型大小,一个上面形式的函数指针:可以传这个样式的比较函数,满足条件时返回负值,不满足条件时传正值并改变元素顺序)
    qsort(arr,n,sizeof(float),cmp);
    print_array(arr,n);
}

示例(字符串排序)

#include<stdio.h>//printf()
#include<stdlib.h>//qsort()
#include<string.h>//strcmp()

void print_array(char* arr[],int n){
   char** p =arr;
   while(p<arr+n){
      printf("%s ",*p++);
   }
   printf("\n");
}

int cmp(const void* p,const void*q){
    char* a = *(char**)p;
    char* b = *(char**)q;
    return strcmp(a,b);
}

int main(int argc,char* argv[]){
    print_array(argv,argc);
    qsort(argv,argc,sizeof(char*),cmp);
    print_array(argv,argc);
}

C初阶7:结构体

struct Point{
   int x;
   int y;
   int z;
};//占3*4=12个字节
  1. 结构体定义对象 struct Point p ={1,3,6};
  2. 结构体地址是它第一个成员的地址
  3. 结构体指针:struct Point* q =&p;
    p->x等同于(*q).x 注意:->自带解引用
    3.1 访问结构体对象成员. 如:p.x=100
    3.2 访问结构体指针成员用-> 如:q->x=1000;
  4. c语言中定义结构体对象时 struct不可省略 ,如struct Point q;

示例:

#include<stdio.h>

struct Point{
   int x;
   int y;
   int z;
};//占3*4=12个字节

void Point_Print(struct Point p){
   printf("(%d,%d,%d)\n",p.x,p.y,p.z);
}

void PointPtr_Print(struct Point* p){
    printf("(%d,%d,%d)\n",p->x,p->y,p->z);
}

int main(){
    struct Point p ={1,3,6};
    Point_Print(p);
    printf("%d\n",sizeof(p));
    struct Point* q=&p;//q是个指针,在64位中占8个字节,32位占4个字
节
    printf("%d\n",sizeof(q));
    Point_Print(*q);
    q->x=100;
    q->y=300;
    q->z=600;
    Point_Print(p);
    PointPtr_Print(q);
}
  1. 结构体数组struct Point Points[] = {{1,2,3},{3,3,3},{2,3,4},{7,7,7}};
  2. 结构体嵌套结构体
struct Line{
     struct Point p1;
     struct Point p2;
}

示例:

#include<stdio.h>
#include<math.h>  //sqrt()  pow(a,b)   编译时要链接库用-lm
struct Point{
   int x;
   int y;
   int z;
};//占3*4=12个字节

//结构体嵌套结构体
struct Line{
    struct Point p1;
    struct Point p2;
};

float GetDistance(struct Line line){
     return sqrt(
                     pow(line.p1.x-line.p2.x,2)+
                     pow(line.p1.y-line.p2.y,2)+
                     pow(line.p1.z-line.p2.z,2)//pow(a,b)表示a的b>次方
                     );
}
float GetDistance2(struct Line* line){//参数是结构体指针,访问其对
象line成员时要用->   调用是传参要传结构体的地址
     return sqrt(
                     pow(line->p1.x-line->p2.x,2)+
                     pow(line->p1.y-line->p2.y,2)+
                     pow(line->p1.z-line->p2.z,2)//pow(a,b)表示a的
b次方
                     );
}
void Point_Print(struct Point p){
   printf("(%d,%d,%d)\n",p.x,p.y,p.z);
}

void PointPtr_Print(struct Point* p){
    printf("(%d,%d,%d)\n",p->x,p->y,p->z);
}

int main(){
    struct Point p ={1,3,6};
    Point_Print(p);
    printf("%d\n",sizeof(p));
    struct Point* q=&p;//q是个指针,在64位中占8个字节,32位占4个字
节
    printf("%d\n",sizeof(q));
    Point_Print(*q);
    q->x=100;
    q->y=300;
    q->z=600;
    Point_Print(p);
    PointPtr_Print(q);

   //结构体数组,遍历如同数组遍历
    struct Point points[] = {{1,2,3},{3,3,3},{2,3,4},{7,7,7}};
    for(int i=0;i<4;i++){
       Point_Print(points[i]);
       PointPtr_Print(points+i);
    }

    //结构体嵌套结构体
    struct Line line = {{0,0,0},{3,4,5}};
    Point_Print(line.p1);
    Point_Print(line.p2);
    printf("%f\n",GetDistance(line));
    printf("%f\n",GetDistance2(&line));
}
  1. 结构体包含结构体数组
struct Triangle{
   struct Point p[3];
};
struct Triangel t = {{{1,2,2},{1,1,1},{8,0,0}}};
  1. 遍历
for(int i=0;i<3;i++){
   Point_Print(t.p[i]);
}

示例:

#include<stdio.h>
#include<time.h>  //struct tm*  time() ... 

int main(){
    time_t now = time(NULL);
    struct tm* utc = gmtime(&now);//格林时间 
    struct tm* local = localtime(&now);//本地时间 比格林世界多8个小时

    char temp[20]={'\0'};//  YYYY/MM/DD HH:mm:SS
    //把struct tm*类型的结构体指针对象的内容,以%F年 %T时间的格式(strftime里面定义的),放入大小为20 的temp数组空间中
    strftime(temp,20,"%F %T",local);

    printf("%s\n",temp);

    struct tm test;
    //把时间字符串以%F %T格式放入到 struct tm类型的结构体对象test中
    strptime("2020-01-01 21:03:10","%F %T", &test);
    printf("%d/%d/%d %d:%d:%d\n",test.tm_year+1900,test.tm_mon+1,test.tm_hour,test.tm_min,test.tm_sec);
}

C初阶8:联合体

示例:

#include<stdio.h>

struct SPoint{
   int x;
   int y;
   int z;
};//占3个*4字节=12字节内存
union UPoint{
   int x;
   int y;
   int z;
};//联合体成员共享内存,所占空间为最大的成员所占空间 这里占4个字节,因为int类型占4字节

union Integer{
  int data;
  char bytes[4];
};

int main(){
    printf("%d\n",sizeof(struct SPoint));
    printf("%d\n",sizeof(union UPoint));

    union UPoint up;
    up.x = 100;//给联合体一个成员赋值,因为成员之间共享一块内存,所以其余成员也被赋同样的值
    printf("(%d,%d,%d)\n",up.x,up.y,up.z);

    union Integer n;
    n.data = 10113234;
    printf("%#x\n",n.data);//%#x以十六进制输出
    for(int i=0;i<4;++i){
       printf("%#hhx\n",n.bytes[i]);//%x默认整型
    //单个h是把int(4个字节)扩展成short(2个字节)
    //两个h是把int扩展成char(1个字节)
   }

C初阶:枚举

  1. 魔术数字:程序中含义不明的数字。
  2. 通常用符号来表示,解决方式:const和#define
    const int SUN = 0;//SUM是常量1 用等号
    #define SUN 0;//把SUN在预处理时替换成0,用空格 -E可以看预处理
  3. const#define(宏定义)区别:
No. 比较项 #define const
1 编译处理 预处理阶段 编译、运行阶段
2 工作原理 简单的字符串替换 有对应的数据类型
3 存储方式 展开,在内存中有若干个备份 只读变量在内存中只有一份
4 类型检查 没有类型安全检查 在编译阶段进行类型检查
5 作用域 从定义开始,任何位置都可访问 只能在变量作用域内

#define MON 1;在编译预处理时把所有的MON替换成1
4. 宏定义无类型
const在编译运行阶段编译处理

  1. 枚举:enum 枚举名{名字0,名字1,名字2,...}
    5.1. 类型为int 值一次从0到n
    5.2. 枚举可看作一堆宏定义放一起(但不是) 就是给一些常量值,规定一个名字
    5.3. 枚举量可以直接作为值使用
    5.4. 枚举类型可以直接作为类型使用,用类型定义时只能用枚举值里的有限个常量
    5.5. 声明时可以指定值 如:
enum R{Bin=2,Oct=8,Dec=10,Hex=16};

如确定一个值后续依次加一 如:

enum M{Jan=1,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec};

即:Feb=2,Mar=3

上一篇:296. Best Meeting Point 到所有人家距离之和最短的点


下一篇:如何更好地训练神经网络训练