c语法进阶

数组参数传递

我们先看个例子,main入口 ,同样的代码打印的信息不一样。



#include "stdafx.h"
#include "stdio.h"



void print2(int* arr) {
    int len = sizeof(arr) / sizeof(int);
    printf("len =%d\n", len);//len = 1  
    int i = 0;
    for (; i < len; i++) {
        printf("%d\n", arr[i]);
    }
}

void print(int arr[]) {
    int len = sizeof(arr) / sizeof(int);
    printf("len =%d\n", len);//len = 1  
    int i = 0;
    for (; i < len; i++) {
        printf("%d\n", arr[i]);
    }
}

int main()
{
    int arr[] = { 1,2,3,4,5 };
    int len = sizeof(arr) / sizeof(int);
    printf("len =%d\n", len);//len = 5
    print(arr);
    printf("===================\n");
    print2(arr);
    printf("===================\n");
    getchar();
    return 0;
}

从这demo我们可以看到,数组作为参数传递的时候为什么获取不到长度?

数组作为参数传递,会退化成为一个指针,传递的是首地址 (高效)

数据类型剖析

  1. 数据类型本质: 一块连续大小的内存空间
    demo1
int a; //告诉c和c++编译器开辟一块连续大小的4字节的内存空间
a =32;

demo2

int main()
{
    int arr[] = { 1,2,3,4,5 };//内存大小空间 4x5=20
    printf("%d , %d,%d ,%d", arr,arr+1,&arr,&arr+1);// 17758788 ,17758792,17758788 ,17758808
    getchar();
    return 0;
}

  1. 数据类型别名
/* Primitive types that match up with Java equivalents. */
typedef uint8_t  jboolean; /* unsigned 8 bits */
typedef int8_t   jbyte;    /* signed 8 bits */
typedef uint16_t jchar;    /* unsigned 16 bits */
typedef int16_t  jshort;   /* signed 16 bits */
typedef int32_t  jint;     /* signed 32 bits */
typedef int64_t  jlong;    /* signed 64 bits */
typedef float    jfloat;   /* 32-bit IEEE 754 */
typedef double   jdouble;  /* 64-bit IEEE 754 */

/* "cardinal indices and sizes" */
typedef jint     jsize;

  1. void* 代表任意的数据类型的指针

变量的本质

  1. 变量的本质:固定内存块大小(一块连续大小的内存空间)的别名,通过变量去操作一块内存上的数据
  2. 变量的三要素:内存大小,名称,作用域

内存四驱模型

  1. 程序运行流程
  1. 操作系统会将物理磁盘上的代码load到内存
  2. 加载到内存后会将c代码分为4个区
  3. 后系统找到main程序入口运行
  1. 四驱模型
  1. 栈区: 由编译器自动分配的,存放一些局部变量和函数,内存会自动进行回收。
  2. 堆区: 一般由开发者自己开辟的,内存需要手动释放 malloc-->free new-->delete等。
  3. 全局区(数据区): 静态的一些常量,字符串等。
  4. 程序代码区: 存放函数的二进制代码。

struct Student{
    int age;
};

typedef Student _Student;

void change(int number) {
    number = 300;
    printf("%p\n", &number);//00B3F928
}

void change(int* p_number) {
    *p_number = 300;
    printf("%p\n", p_number);//00B3F9FC
}
void chanegStudentAge(_Student student) {
    student.age = 20;
    printf("%p\n", &student);//0135F74C
}

void main() {
    int a = 10;
    a = 20;
    change(a);//把a的值复制给了number 
    printf("a =%d\n",a);  //a = 20

    change(&a);//直接操作a的地址
    printf("a =%d\n", a);  //a = 300


    _Student student =  _Student();
    student.age = 5;
    printf("student =%d\n", &student);  //student =20314144
    chanegStudentAge(student);
    printf("age = %d", student.age);
    getchar();
}

不管是基本数据类型还是结构体,c都是值传递,和java不同的是,java基本数据类型是值传递,对象是引用传递。所以在c当中一般都是指针传递

change(int number)


image

changep(int* p_number)


image

栈的开口方向

int a=5;//先入栈
int buffer[]={1,2,3};
int b=7;

fprint("%p , %p",&a,&b);


如果地址a>b(debug),说明开口向下。
a<b(release)开口向上。
buffer数据类型的方向和栈的开口方向没关系,都是向上。&buffer[0]>&buffer[1]

指针强化

指针也是一种数据类型,虽然占用四个字节,但是有数据类型。

野指针

struct Student{
    int age;
};



void main() {
     Student* student =(Student*)malloc(sizeof(Student));

     //释放的时候记得判断为NULL
     if (student != NULL) {
         free(student);//这个时候student变为野指针
         //我们不需要的话最好置为NULL
         student = NULL;
     }

    
     //释放的时候记得判断为NULL,这个时候就不会去free,从而避免错误
     if (student != NULL) {
         free(student);
     }
     getchar();
}

NULL
NULL也是一个内存指针,指针指向00000000,只不过我们不能进行操作。


 printf("NULL=%p", NULL); //NULL=00000000
 

字符串和 buffer 强化


void main() {
    char buffer[100] = {'1','2','3'};//后面3-99 都是默认值0

    //长度
    printf(" strlen(buffer) = %d\n", strlen(buffer));//strlen(buffer) = 3  根据\0结束计算
    //内存
    printf(" sizeof(buffer) = %d\n", sizeof(buffer));//sizeof(buffer) = 100
    //内容
    printf("%d %d %d \n",buffer[0], buffer[3], buffer[75]);//49 0 0

    printf("-------------------------------\n");

    char buffer1[] = { '1','2','3' };
    //长度
    printf(" strlen(buffer1) = %d\n", strlen(buffer1));//strlen(buffer1) = 15  根据\0结束计算
    //内存
    printf(" sizeof(buffer1) = %d\n", sizeof(buffer1));//sizeof(buffer1) = 3
    //内容
    printf("%d %d %d \n", buffer1[0], buffer1[3], buffer1[8]);//49 -52 -52

    //char buffer2[2] = { '1','2','3' };   这个直接就报错了 编译不通过

    printf("-------------------------------\n");
    char buffer3[100] = { '1','2','\0', '2' };//后面4-99 都是默认值0
     //长度
    printf(" strlen(buffer3) = %d\n", strlen(buffer3));//strlen(buffer3) = 2  根据\0结束计算
    //内存
    printf(" sizeof(buffer3) = %d\n", sizeof(buffer3));//sizeof(buffer3) = 100
    //内容
    printf("%d %d %d %d %d \n", buffer3[0], buffer3[1], buffer3[2], buffer3[3], buffer3[8]);//49 50 0 50 0


    printf("-------------------------------\n");
    char buffer4[100] = { '0' };//把数据初始化为0
    //长度
    printf(" strlen(buffer4) = %d\n", strlen(buffer4));//strlen(buffer4) = 1  根据\0结束计算
    //内存
    printf(" sizeof(buffer4) = %d\n", sizeof(buffer4));//sizeof(buffer4) = 100
    //内容
    printf("%d %d %d %d %d \n", buffer4[0], buffer4[1], buffer4[2], buffer4[3], buffer4[8]);//48 0 0 0 0

    printf("-------------------------------\n");
    char buffer5[100] ; //数据全部默认为-52
    //长度
    printf(" strlen(buffer5) = %d\n", strlen(buffer5));//strlen(buffer5) = 109  根据\0结束计算
     //内存
    printf(" sizeof(buffer5) = %d\n", sizeof(buffer5));//sizeof(buffer5) = 100
     //内容
    printf("%d %d %d %d %d \n", buffer5[0], buffer5[1], buffer5[2], buffer5[3], buffer5[8]);//-52 -52 -52 -52 -52

    printf("-------------------------------\n");
    //char *buffer6 = "hello"; //和下面一样的
    char buffer6[] = "hello"; //  相当于"hello\0"

    //char * ,char[],malloc区别:前两个都是在栈里开辟内存,字符串放在常量区,数组定义的是将常量区的数据copy过来到char[]里面(这样的话栈里也有了数据),char*是直接指针指向常量,malloc的方式是在堆里面开辟内存,存储数据到堆里
     //长度
    printf(" strlen(buffer6) = %d\n", strlen(buffer6));//strlen(buffer6) = 5  根据\0结束计算
     //内存
    printf(" sizeof(buffer6) = %d\n", sizeof(buffer6));//sizeof(buffer6) = 6
    //内容
    printf("%d %d %d %d %d \n", buffer6[0], buffer6[1], buffer6[2], buffer6[3], buffer6[8]);//104 101 108 108 -52

    getchar();
}

char * ,char[],malloc区别:前两个都是在栈里开辟内存,字符串放在常量区,数组定义的是将常量区的数据copy过来到char[]里面(这样的话栈里也有了数据),char*是直接指针指向常量,malloc的方式是在堆里面开辟内存,存储数据到堆里。从而可以知道,字符串可以在任何区域开辟内存。
如图,栈中从上往下依次是char[],char*,malloc()

image

开发模型强化

  1. 确定参数,传递指针
  2. 一定要考略健壮性
  3. 异常错误,抛出说明
  4. 不要直接轻易的去改变调用者传递给你的指针,如需必要可以先考略临时的变量操作。可以加上const修饰参数强制不可修改。
struct AndroidBitmapInfo{
    int width;
    int height;
    int format;
};


int androidBitmapGetInfo(AndroidBitmapInfo * pAndroidBitmapInfo) {

    //2. 健壮性考略
    if (pAndroidBitmapInfo == NULL) {
        printf("pAndroidBitmapInfo is null");
        //3. 异常错误,抛出说明
        return -1;
    }

    //4. 不要直接轻易的去改变调用者传递给你的指针
    //pAndroidBitmapInfo = &AndroidBitmapInfo();

    pAndroidBitmapInfo->height = 1920;
    pAndroidBitmapInfo->width = 1080;
    return 0;
}



void main() {
    //1. 确定参数,传递指针
    AndroidBitmapInfo androidBitmapInfo;
    int ret =androidBitmapGetInfo(&androidBitmapInfo);
    if (ret == 0) {
        printf("w=%d,h=%d", androidBitmapInfo.width, androidBitmapInfo.height);
    }
    
    getchar();
 }

结构体强化

  • c=是赋值操作。
typedef struct {
    char name[50];
    int age;
}Student;

void main() {
    Student stu1 = {"张三",35};
    Student stu2;
    stu2 = stu1;
    printf("%p , %p \n", &stu1, &stu2);//007CFD7C , 007CFD3C
    printf("%s , %d \n", stu2.name, stu2.age);//张三 , 35
    getchar();
 }

  • 在结构体里面套指针

#include "stdafx.h"
#include "stdio.h"
#include <string.h>
#include <Windows.h>

//结构体套指针,必须要对结构体指针进行初始化(赋值)
//指针可以指向任何地址,但是它的大小永远是4字节

typedef struct {
    char* name;// 定义了一个name指针
    int age;
}Student;

void main() {
    Student stu;
    // 1. 
    //stu.name = "李四"; // 将指针指向常量区的"李四"

    //2. 
    //往stu.name里面copy数据 "李四" 不能直接copy
    //strcpy(stu.name, "李四");

    //3.
    //首先把char* name 指向堆区开辟的内存 ,然后把常量区的数据"李四" copy到堆里面
    stu.name =(char*) malloc(100);
    strcpy(stu.name, "李四");

    stu.age = 35;
    printf("%s , %d \n", stu.name, stu.age);//李四 , 35
    getchar();
 }

  • copy和浅copy


typedef struct {
    char *name;
    int age;
}Student;



void copyToP(Student* from, Student *to) {
    //浅copy
    *to = *from;

    //深copy
    to->name = (char*)malloc(100);//开辟内存
    strcpy(to->name, from->name);//copy值
};


void main() {
    char * name =(char*) malloc(100);
    Student stu = {name,25};
    strcpy(stu->name, "张三");
    stu.age = 35;
    printf("%s , %d \n", stu.name, stu.age);//张三 , 35

    Student stu2 = {"李四",15};
    copyToP(&stu2,&stu);
    printf("%s , %d \n", stu2.name, stu2.age);//张三 , 35

    if (stu.name) {
        free(stu.name);
        stu.name = NULL;
    }

    if (stu2.name) {//如果是浅copy会释放出错,因为stu.name和stu2.name指向一个位置  stu.name已经free了
        free(stu2.name);
        stu2.name = NULL;
    }


    getchar();
 }
  • 结构体的偏移量 :见c基础的结构体内存开辟以及内存计算
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • C语言中内存分配 在任何程序设计环境及语言中,内存管理都十分重要。在目前的计算机系统或嵌入式系统中,内存资源仍然是...
    一生信仰阅读 4,988评论 0 2
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,916评论 1 32
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 6,502评论 0 7
  • 【小魔仙儿】28171116学习力7碱性D37 好久没玩手指谣了,宝玩玩具时,我自己念,一会念到《谁会爬》时她凑过...
    风轻云淡_adc3阅读 1,378评论 0 0
  • 昆明梁艳分享262天。网络中级五期。2018.02.06 今天参加社区春节前的组织活动,作为一名非在职社区党...
    诗心小鹿阅读 1,292评论 1 0

友情链接更多精彩内容