详解C语言中的函数、数组与指针

1、函数:当程序很小的时候,我们可以使用一个main函数就能搞定,但当程序变大的时候,就超出了人的大脑承受范围,逻辑不清了,这时候就需要把一个大程序分成许多小的模块来组织,于是就出现了函数概念;

      函数是C语言代码的基本组成部分,它是一个小的模块,整个程序由很多个功能独立的模块(函数)组成。这就是程序设计的基本分化方法;

  (1) 写一个函数的关键:

    函数定义:函数的定义是这个函数的实现,函数定义中包含了函数体,函数体中的代码段决定了这个函数的功能;

    函数声明:函数声明也称函数原型声明,函数的原型包含三部分:函数名,返回值类型,函数参数列表,函数的声明是告诉使用函数的人,这个函数使用时应该传递给他什么样的参数,

         它会返回什么样类型的返回值。这些东西都是写函数的人在函数定义中规定好的,如果使用函数的人不参照这个原型来使用,就会出错,结果就会和你想的不一样;

    函数调用:函数调用就是使用函数名来调用函数完成功能。调用时必须参照原型给函数传参,然后从函数得到适当的返回值作为结果;

  (2) 函数的参数:函数调用的过程,其实就是实参传递给形参的一个过程。这个传递像是一次拷贝,实参(本质是一个变量)本身并没有进入到函数内,而是把自己的值复制了一份传给了函数中的形参,

          在函数中参与运算,这种传参方法,就叫做传值调用;

    形参:形式参数,在函数定义和函数声明中的参数列表中的参数都是形参;

    实参:实际参数,函数调用中,实际传递的参数才是实参。

  (3) 返回值(关键字return):当函数执行完之后,会给调用该函数的地方返回一个值。这个值的类型就是函数声明中返回值类型,这个值就是函数体中最后一句return xxx;返回的那个值;

  (4) 函数名:取函数名要注意以下几点:

    第一,起名字时候不能随意,要符合规则,而这个规则分别有两个层次,即第一层是合法,第二层是合理。合法就是符号C语言中变量名的命名规则,合理就是变量名起的好,

        人一看就知道什么意思,一看就知道这个函数是干嘛的;

    第二,C语言中,所有的符号都是区分大小写的;

    第三,C语言函数名的命名习惯。这个没有固定的结论,有多种使用都很广泛的命名方式如下:

        linux的命名习惯:student_age  str_to_int

        骆驼命名法:studentAge  strToInt

    注:想进一步了解可以参考林锐的《高质量程序设计指南》;

// 简单计算器
#include <stdio.h>
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
int main(void)
{
  int a, b, c;
  a = 54;
  b = 32;
  c = add(a, b);
  //c = sub(a, b);
  //c = mul(a, b);
  //c = div(a, b);
  printf("c = %d.\n", c);
  printf("a - b = %d.\n", sub(a, b));
  return 0;
}
int add(int a, int b)
{
  return a + b;
}
int sub(int a, int b)
{
  return a - b;
}
int mul(int a, int b)
{
  return a * b;
}
int div(int a, int b)
{
  return a / b;
}

2、数组:数组就是若干个数组成的一个组,数就是一个特定数据类型的变量,组就是说好多数放在了一起;

  (1) 数组的定义:

    int a[10];   数组中元素类型 数组名[数组元素个数];

    注:数组中的所有元素都是同一种数据类型,不可能在一个数组中出现两种数据类型的数。

  (2) 数组的使用:数组定义的时候是作为一个整体来定义的,但是使用的时候不能作为一个整体来使用,使用时必须拆开使用数组中的各个元素;

    如:数组int a[10],使用其中的十个元素,分别用a[0]……a[9],其中[]是数组的标志,[]中的数字叫做数组的下标(index,索引),下标是我们访问数组中各个元素的指引,

      下标是0代表数组中第一个元素,下标是1代表数组第二个元素,以此类推,若数组长度为n,则下标中最后一个是n-1;

    注:访问数组时要特别注意下标,下标是从0开始的,如果下标超出了n-1,会产生越界访问,结果是不可预期的;

  (3) 数组的初始化:初始化是为了让对象有一个预定的初始状态,数组的初始化分两种:

    第一种:完全初始化。依次赋值;

    第二种:不完全初始化。初始化式中的值从a[0]开始,依次向后赋值,不足的默认用0填充赋值

  (4) 不同数据类型的数组: 

 int a[3];      // 整型数组
    float b[3];     // 单精度浮点型数组
    double c[3];    // 双精度浮点型数组
    char d[3];     // 字符型数组

  (5) 字符数组:在C语言中引用一个单个字符时,应该用单引号''括起来,譬如'a';

    字符数组的初始化:定义数组同时初始化,则可以省略数组定义时[]中的长度。C语言编译器会自动推论其长度,推论依据是初始化式中初始化元素的个数;

    引用字符串:在C语言中引用一个字符串时,应该用""括起来,如"abcde",其中"abcde"实际上有6个字符,分别是'a' 'b' 'c' 'd' 'e' '\0';

          '\0' 是C语言中定义的字符串的结尾标志,这个字符是ASCII码表中的第一个字符,它的编码值是0,对应的字符是空字符(不可见字符);

// 数组的演示
#include <stdio.h>
int main(void)
{
  int a[4];
  a[0] = 23;
  a[1] = 54;
  a[2] = 98;
  a[3] = 74;
  printf("a[1] = %d, a[3]= %d.\n", a[1], a[3]);
  return 0;
}
// 字符数组的演示
#include <stdio.h>
int main(void)
{
/*
  int i = 0;
  //char a[5];
  //char a[5] = {'a', 'b', 'c', 'd', 'e'};
  //char a[5] = {97, 98, 99, 100, 101};
  //char a[] = {97, 98, 99, 100, 101};
  char a[] = "abcde";
  for (i=0; i<5; i++)
  {
    printf("a[%d] = %d  %c\n", i, a[i], a[i]);
  }
*/
  int i = 0;
  char a[] = {97, 98, 99, 100, 101};
  char b[] = "abcde";
  printf("sizeof(a) = %d, sizeof(b) = %d.\n", sizeof(a), sizeof(b));
  return 0;
}

3、指针:全称是指针变量,其实质是C语言的一种变量。这种变量比较特殊,通常它的值会被赋值为某个变量的地址值(p = &a),然后我们可以使用*p这样的方式去间接访问p所指向的那个变量;

  (1) 指针存在的意义:可以间接访问。有了指针之后,我们访问变量a不必只通过a这个变量名来访问。而可以通过p = &a; *p = xxx;这样的方式来间接访问变量a;

  (2) 两个重要的运算符:&和*

    &:取地址符,将它加在某个变量前面,则组合后的符号代表这个变量的地址值;

    如: int a; int *p; p = &a; 则将变量a的地址值赋值给p

      a      代表变量a本身

      p      代表指针变量p本身

      &a    代表变量a的地址值

      *p    代表指针变量p所指向的那个变量,也就是变量a

      &p    代表指针变量p本身的地址值,符号虽合法,但对题目无意义

      *a    把a看作一个指针,*a表示这个指针所指向的变量,该符号不合法

    *:指针符号。指针符号在指针定义和指针操作的时候,解析方法是不同的;

      int *p; 定义指针变量p,这里的*p含义不是代表指针变量p所指向的那个变量,在定义时这里的*含义是告诉编译器p是一个指针

      *p = 0x24; 使用指针的时候,*p则代表指针变量p所指向的那个变量

  (3) 指针的定义和初始化:

    第一种:先定义再赋值

        int *p;    // 定义指针变量p

        p = &a;    // 给p赋值

    第二种:定义的同时初始化

        int *p = &a;  // 效果等同于上面的两句

  (4) 各种不同类型的指针:指针变量本质上是一个变量,指针变量的类型属于指针类型。int *p;定义了一个指针类型的变量p,这个p所指向的那个变量是int型;

    int *pInt;      // pInt是指针变量,指向的变量是int类型

    char *pChar;    // pChar是指针类型,指向的变量是char类型

    float *pFloat;

    double *pDouble;

    注:各种指针类型和它们所指向的变量类型必须匹配,否则结果不可预知;

  (5) 指针定义的两种理解方法:

    int *p;

    第一种(推荐):首先看到p,这个是变量名;其次,p前面有个*,说明这个变量p是一个指针变量;最后,*p前面有一个int,说明这个指针变量p所指向的是一个int型数据;

    第二种:首先看到p,这个是变量名;其次,看到p前面的int *,把int *作为一个整体来理解,int *是一种类型(复合类型),该类型表示一种指向int型数据的指针;

  (6) 指针与数组的初步结合:

    数组名:做右值时,数组名表示数组的首元素首地址,因此可以直接赋值给指针;

    如:int a[10];其中a和&a[0]都表示数组首元素a[0]的首地址,而&a则表示数组的首地址;

    注:数组首元素的首地址和数组的首地址是不同的,前者是数组元素的地址,而后者是数组整体的地址。两个东西的含义不同,但是数值上是相同的;

    根据以上,我们知道可以用一个指针指向数组的第一个元素,这样就可以用间接访问的方式去逐个访问数组中的元素,这样访问数组就有了两种方式:

    有 int a[5];  int *p; p = a;

    数组的方式依次访问:a[0]  a[1]  a[2]  a[3]  a[4]

    指针的方式依次访问:*p  *(p+1)  *(p+2)  *(p+3)  *(p+4)

  (7) 指针与++ --符号进行运算:指针本身也是一种变量,因此也可以进行运算,但是因为指针变量本身存的是某个其他变量的地址值,因此该值进行* / %等运算是无意义的,

                 故两个指针变量相加本身也无意义,但相减有意义。指针变量+1,-1是有意义的,+1就代表指针所指向的格子向后挪一格,-1代表指针所指向的格子向前挪一格。

    *p++的解析:++先跟p结合,但是因为++后置的时候,本身含义就是先运算后增加1(运算指的是p++整体与前面的*进行运算;增加1指的是p+1),所以实际上*p++符号整体对外表现的

            值是*p的值,运算完成后p再加1;

    *p++等同于:*p;   p += 1;

    *++p等同于:p += 1; *p;

    (*p)++,使用()强制将*与p结合,只能先计算*p,然后对*p整体的值++

    ++(*p),先*p取值,再前置++,该值+1后作为整个表达式的值

// 指针的定义、赋值及初始化
#include <stdio.h>
int main(void)
{
  int a = 23;
  int *p = &a;
  *p = 111;
  printf("a = %d\n", a);
  return 0;
}
// 用指针去访问数组
#include <stdio.h>
int main(void)
{
  int a[5] = {555, 444, 333, 222, 111};
  int *p;
  //p = &a;
  //p = &a[0];
  p = a;
  //p += 1;
  //printf("*p = %d.\n", *p);
  //printf("*p++ = %d.\n", *p++);
  //printf("*++p = %d.\n", *++p);
  //printf("(*p)++ = %d.\n", (*p)++);
  printf("++(*p) = %d.\n", ++(*p));
  return 0;
}

4、补充:变量与数据类型的实质

  (1) 程序在环境中运行时,需要一定的资源支持,而这些资源包括:CPU(运算能力)、内存等,这些资源一般由运行时环境(一般是操作系统)来提供,比如我们在linux系统上./a.out运行程序时,

    linux系统为我们提供了运算能力和内存;

  (2) 程序越庞大,运行时消耗的资源越多,比如说内存额定占用,如果越大的程序,占用的内存肯定越多,而占用内存的其中之一,就是我们在程序中定义的变量;

  (3) C语言程序中,变量的实质就是内存中的一个格子。当我们定义了一个变量后,就相当于在内存中得到了一个格子,而这个格子的名字就是变量名,

    以后访问这个内存格子就只用使用该变量名就行了,这就是变量的本质;

  (4) 数据类型的实质是内存中格子的不同种类。比如在32位的机器上:

    短整形格子(short)          占用2字节,即16位的空间

    整形格子(int)             占用4字节,即32位的空间

    单精度浮点型格子(float)         占用4字节,即32位的空间

    双精度浮点型格子(double)        占用8字节,即64位的空间

    字符型格子(char)           占用1字节,即8位的空间

  (5) sizeof运算符:返回一个变量或者一个数据类型的内存占用长度,以字节为单位;

// sizeof运算符的演示
#include <stdio.h>
int main(void)
{
  int len;
  //len = sizeof(int);
  //len = sizeof(float);
  //len = sizeof(double);
  //len = sizeof(char);
  //double d;
  //len = sizeof(d);
  int a[5];
  //len = sizeof(a);
  len = sizeof(a) / sizeof(a[0]);
  printf("len = %d.\n", len);
  return 0;
}

以上所述是小编给大家介绍的C语言中的函数、数组与指针,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • C语言 用指针作为函数返回值详解

    C语言允许函数的返回值是一个指针(地址),我们将这样的函数称为指针函数.下面的例子定义了一个函数 strlong(),用来返回两个字符串中较长的一个: #include <stdio.h> #include <string.h> char *strlong(char *str1, char *str2){ if(strlen(str1) >= strlen(str2)){ return str1; }else{ return str2; } } int main(){ cha

  • C语言中函数指针的三种使用方法总结

     C语言中函数指针的三种使用方法总结 在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 定义方式:int (*p)(int x, int y); 实现代码: #include <stdio.h> int sum(int x, int y){ return x + y; } int reduce(int x, int y){ return x - y; } int

  • C语言中枚举与指针的实例详解

     C语言中枚举与指针的实例详解 总结一下, 定义枚举,用typedef enum关键字, 比如 typedef enum{Red,Green,Blue} Color3; 枚举到数值的转换,如果没有指定代表数值就是从0开始算, 比如 Color3 c=Red; printf("%d",c);会显示0, 除非指定 如typedef enum{Red=3,Green=5,Blue=10} Color3; 关于类型指针的定义, 定义的时候在变量名左边加*代表此变量只是一个空指针而已, 若需要赋

  • C语言 一级指针与二级指针详细介绍

    指针的概念 指针就是地址, 利用这个地址可以找到指定的数据 指针就是地址, 那么在使用的时候, 常常会简单的说 指针变量为指针 指针变量就是存储地址的变量 int *p1;// 申请了一个变量, 即在内存中开辟了一块内存, 存储数据 // 开辟了 8 个字节, 在 Mac 下 指针都占 8 个字节 使用指针, 实际上应该说成使用指针变量      1> 算术运算 +1 移动几个字节? 看类型: int *,  long *,  char *     2> 获得地址表示的数据 指针里面存储的是地

  • C语言中 值传递和指针传递实例详解

    C语言中 值传递和指针传递实例详解 在C语言中,函数的参数和返回值的传递方式有两种:值传递和指针传递. 值传递和指针传递初学者总会有一种朦胧的感觉,所以建议把指针传递的概念摸透,才能熟练应用. 值传递示例:x其实是n的一份临时拷贝,所以并不会改变n的值. #include <stdio.h> #include <windows.h> void Fun(int x) { x = 1; } int main() { int n = 2; Fun(n); printf("%d\

  • C语言指针应用简单实例

    C语言指针应用简单实例 这次来说交换函数的实现: 1. #include <stdio.h> #include <stdlib.h> void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a = 10, b = 20; printf("交换前:\n a = %d, b = %d\n", a, b); swap(a, b); printf("交换

  • C语言 结构体和指针详解及简单示例

    指针也可以指向一个结构体,定义的形式一般为: struct 结构体名 *变量名; 下面是一个定义结构体指针的实例: struct stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在小组 float score; //成绩 } stu1 = { "Tom", 12, 18, 'A', 136.5 }; //结构体指针struct stu *pstu = &stu1; 也可以在定义结构体的同时定义结构

  • 详解C语言中的函数、数组与指针

    1.函数:当程序很小的时候,我们可以使用一个main函数就能搞定,但当程序变大的时候,就超出了人的大脑承受范围,逻辑不清了,这时候就需要把一个大程序分成许多小的模块来组织,于是就出现了函数概念:   函数是C语言代码的基本组成部分,它是一个小的模块,整个程序由很多个功能独立的模块(函数)组成.这就是程序设计的基本分化方法: (1) 写一个函数的关键: 函数定义:函数的定义是这个函数的实现,函数定义中包含了函数体,函数体中的代码段决定了这个函数的功能: 函数声明:函数声明也称函数原型声明,函数的原

  • 详解C语言中的字符串数组

    在C语言当中,字符串数组可以使用: char a[] [10]; 或者 char *a[]; 表示 第一种表示方式固定了每个字符串的最大大小.第二种没有字符串的大小限制. #include <stdio.h> #include <string.h> //该程序的功能是 输入阿拉伯数字的月份数 输出英文月份 int main() { //一个字符串数组 它的下标代表英文月份的阿拉伯数字 char *month[] = {"January","Februa

  • 详解C语言用malloc函数申请二维动态数组的实例

    详解C语言用malloc函数申请二维动态数组的实例 C语言在程序运行中动态的申请及释放内存十分方便,一维数组的申请及释放比较简单. Sample one #include <stdio.h> int main() { char * p=(char *)malloc(sizeof(char)*5);//申请包含5个字符型的数组 free(p); return 0; } 是否申请二维动态内存也如此简单呢?答案是否定的.申请二维数组有一下几种方法 Sample two /* 申请一个5行3列的字符型

  • 详解C语言中动态内存管理及柔性数组的使用

    目录 一.malloc 二.free 三.calloc 四.realloc 1.realloc在扩容时的情况 2.realloc也能实现malloc功能 五.使用动态内存的常见错误 1.free空指针 2.对动态开辟的空间越界访问 3.对非动态开辟内容free 4.只free动态开辟空间的一部分 5.对同一块内存多次free 6.动态内存空间忘记释放(内存泄漏) 六.柔性数组 1.柔性数组的概念 2.柔性数组的特点 3.柔性数组的使用场景 4.柔性数组的优点 一.malloc 这个函数向堆区申请

  • 详解Go语言中数组,切片和映射的使用

    目录 1.Arrays (数组) 2.切片 2.1 make创建切片 3.映射Map Arrays (数组), Slices (切片) 和 Maps (映射) 是常见的一类数据结构 1.Arrays (数组) 数组是定长的. 长度不可改变. 初始化 package main import ( "fmt" ) func main() { var scores [10]int scores[0] = 99 fmt.Printf("scoers:%d\n", scores

  • 详解Go语言Slice作为函数参数的使用

    目录 前言 问题与解析 典型问题 其它疑问1 其它疑问2 结论 参考链接 前言 首先要明确Go语言中实质只有值传递,引用传递和指针传递是相对于参数类型来说. 个人认为上诉的结论不对,把引用类型看做对指针的封装,一般封装为结构体,结构体是值类型,所以感觉都是值传递.不然我感觉其它语言实质不也都是值传递?不过我刚学Go,可能还没完全弄懂,这个有问题可以互相讨论下. Go语言中的值类型:int.float.bool.array.sturct等,声明一个值类型变量时,编译器会在栈中分配一个空间,空间里存

  • 详解C语言中二分查找的运用技巧

    目录 基础的二分查 查找左侧边界 查找右侧边界 二分查找问题分析 实例1: 爱吃香蕉的珂珂 实例2:运送包裹 前篇文章聊到了二分查找的基础以及细节的处理问题,主要介绍了 查找和目标值相等的元素.查找第一个和目标值相等的元素.查找最后一个和目标值相等的元素 三种情况. 这些情况都适用于有序数组中查找指定元素 这个基本的场景,但实际应用中可能不会这么直接,甚至看了题目之后,都不会想到可以用二分查找算法来解决 . 本文就来分析下二分查找在实际中的应用,通过分析几个应用二分查找的实例,总结下能使用二分查

  • 详解C语言中结构体的使用

    目录 结构体的声明 结构体成员的类型 结构体成员的访问 结构体的声明 结构体的定义:结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量. 举例: //定义结构体类型 struct tag//struct结构体关键字 tag结构体标签 struct tag结构体类型 { //成员变量 char name[20]; short age; char telphone[12]; char sex[5]; }s1,s2,s3;//s1,s2,s3是三个全局结构体变量 int m

  • 详解C语言中双指针算法的使用

    目录 前言 一.最长不含重复字符的子字符串 1.题目要求 2.个人题解 二.和为S的两个数字 1.题目要求 2.个人题解 前言 双指针算法 算法思想 双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的. 换言之,双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算. 今天带大家来学习算法中双指针的应用场景. 一.最长不含重复字符的子字符串 1.题目要求 2.个人题解 2.1

  • 详解C语言中结构体(struct)的用法

    目录 粉丝问答 三种结构体类型变量说明 1. 先定义结构,再定义结构变量 2. 定义结构体类型的同时说明变量 3. 直接说明结构变量 结构体成员表示方法 结构体指针做参数 结构体初始化 定义变量的同时初始化 先定义在初始化 常用初始化 typedef与struct 前置声明 结构体对齐 粉丝问答 有个粉丝在群里问了这样一个问题,问题在图中已经标出,如下图. DQuestsion 头文件的结构体的定义为: typedef struct{ u8 bmRequestType; u8 bRequest;

随机推荐