首页 > 其他分享 >#C语言基础 笔记三

#C语言基础 笔记三

时间:2024-07-03 22:29:01浏览次数:3  
标签:return int 基础 笔记 C语言 char 地址 printf hello

 指针和数组

直接访问:按变量的地址存取变量的值(通过数组名访问)
间接访问:通过存放变量的地址的变量去访问元素(通过指针访问)

1. 指针和一维数组

int a[5] = {1, 2, 3, 4, 5};

int *p = a;

直接访问:

int a[5] = {5, 4, 3, 2, 1};
    int *p = a;
    printf("%p %p %p\n", a, a+1, a+2);
    printf("%p %p %p\n", p, p+1, p+2);
    printf("%d %d %d\n", a[0], *(a+1), *(a+2));
间接访问:

int a[5] = {5, 4, 3, 2, 1};
    int *p = a;
    printf("%p %p %p\n", a, a+1, a+2);
    printf("%p %p %p\n", p, p+1, p+2);
    printf("%d %d\n", *(a+2), *(a+3));
    printf("%d %d\n", *(p+2), *(p+3));
    printf("%d %d\n", a[0], p[0]);
a和p本质上不同,a是地址常量,p是变量,a不能执行++操作,但是p可以

访问数组元素a[i]的值:
直接访问:a[i] *(a+i)
间接访问:p[i] *(p+i)

访问数组元素a[i]的地址:
直接访问:&a[i] a+i
间接访问:&p[i] p+i

int a[3] = {3, 2, 1};

int *p = a;

printf("%d\n", *p++); // 3 再打印一次的话就是2

printf("%d\n", *a++); // 错误,a地址常量

运算方法:

1) ++和 * 都是单目运算符,优先级相同

2) 运算顺序从右向左进行运算

int a[3] = {3, 2, 1};

int *p = a;

printf("%d\n", 下列打印);

*(p++) // 3 实际上指针指向第二个元素的地址

(*p)++ // 打印出来的是3 实际上第一个元素值变为4

++*p // 打印出来的是4 自加完之后的值

++(*p) // 同上

*++p // 2,先将会指针向高地址方向移动一个数据单位,然后取地址内容

*(++p) // 同上

2. 指针和二维数组

int a[2][3] = {1, 2, 3, 4, 5, 6}; // a数组名,表示第一行首地址,a+1:第二行首地址

在a前面加 *,表示将行地址降级为列地址

*a:第一行第一列的地址

*a+1:第一行第二列的地址

*(a+1):第二行第一列的地址

*(a+1)+1:第二行第二列的地址

直接访问:

*(*(a+i)+j):拿到i+1行j+1列的元素

*(a[i]+j):拿到i+1行j+1列的元素

a[i][j]:拿到i+1行j+1列的元素

间接访问
数组指针

定义:本质上是指针,指向的数组(行指针)

格式:存储类型 数据类型 (*指针变量名)[列数];

int a[2][3] = {1, 2, 3, 4, 5, 6};

int (*p)[3] = a;

p:int (*)[3]:运算是三个三个算的

p可以代替a进行元素访问,但是本质不同

访问 a[i][j] 地址:

p[i] + j == a[i]+j

*(p+i)+j

访问 a[i][j] 的内容

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

大小:

sizeof(p) == 4字节

例题:

int a[2][3]={1,2,3,4,5,6};用数组指针遍历二维数组。

若有程序段:int a[2][3],(*p)[3]; p=a;则对a数组元素的正确引用是

A) (p+1)[0] B) *(*(p+2)+1) c) *(p[1]+1) D) p[1]+2

下面程序的运行结果是 。

main ( )

{ int x[5]={2,4,6,8,10}, *p, **pp ;

p=x , pp = &p ;

printf(“%d”,*(p++));

printf(“%3d”,**pp);

}

A) 4 4 B) 2 4 C) 2 2 D) 4 6

已有定义int k=2;int *ptr1,*ptr2;且ptr1和ptr2均已指向变量k,下面不能正确执行的赋值语句是 。

A) k = *ptr1 + *ptr2 B) ptr2 = k

C) ptr1 = ptr2 D) k = *ptr1*(*ptr2)

若有语句:int *p,a=4;和p=&a;下面均代表地址的一组选项是

A) a, p, *&a B)&*a, &a, *p C) *&p, *p, &a D) &a, &*p, p

下面程序段的运行结果是 。

char a[ ]=”language” , *p ;

p=a ;

while (*p!=’u’) { printf(“%c”,*p-32); p++ ; }

A) LANGUAGE B) language C) LANG D) langUAGE

以下代码的运行结果是 ( ) (H3C) (printf参数运算顺序)

int main()

{

int arr[]={11,12,13,14,15};

int *ptr = arr;

*(ptr++) += 100; // 展开: *ptr = *ptr +100; ptr++;

printf("%d %d\n",*ptr,*(++ptr));

return 0;

}

A) 13 13 B) 112 13 C) 12 12 D) 12 13

若有定义:int a[2][3];则对a数组的第i+1行第j列+1元素值的正确引用是。

A)*(*(a+i)+j) B)(a+i)[j] C)*(a+i+j) D)*(a+i)+j

若有定义:int a[5],*p=a;则对a数组元素地址的正确引用是。

A)p+5 B)*a+1 C)&a+1 D)&a[0]

指针数组

1. 定义:

本质是数组,里面存放的是指针

2. 格式:

存储类型 数据类型 *数组名[元素个数]

int *arr[3] = {};

3. 应用实例:

3.1. 用于存放普通变量的地址

int a = 10, b = 20, c = 30;

int *arr[3] = {&a, &b, &c};

访问b的地址:

arr[1] *(arr+1)

访问 b 的值

*(arr[1]) *(*(arr+1))

3.2. 用于存放二维数组的每一行第一个元素的地址(列地址)

int a[2][3] = {1, 2, 3, 4, 5, 6};

int *arr[2] = {a[0], a[1]};

访问 a[1][2] 的地址:

arr[1]+2 *(arr+1)+2

arr:第一个元素的地址

arr+1:第二个元素的地址

*(arr+1):第二个元素:a[1]:第二行第一列的地址

*(arr+1)+2:第二行第三列的地址 &a[1][2]

3.3. 用于存放字符串

char str[32] = "hello"; // 存放的是整个hello字符串

char *str = "hello"; // 存放的是hello字符串的首地址

char str[32] = "hello";

char *strs = "hello";

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

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

printf("%p %p\n", strs, &str[0]);

printf("%c %c\n", *(strs+1), str[1]);

使用指针数组存放字符串

char *str[3] = {"hello", "world", "xiaomisu7"};

打印 "xiaomisu7" 字符串

char *str[3] = {"hello", "world", "xiaomisu7"};

printf("%s\n", str[2]); // xiaomisu7

printf("%s\n", *(str+2)); // xiaomisu7

printf("%s\n", *(str+2)+4); // misu7

打印 'm' 这个字符

printf("%c\n", *(*(str+2)+4)); // m

printf("%c\n", *(str[2]+4)); // m

练习:

用指针将整型组s[8]={1,2,3,4,5,6,7,8}中的值逆序存放。

puts 函数用于打印以空字符 '\0' 结尾的字符串,而不是用于打印整数数组

练习:

用变量a给出下面的定义(3C科技、宇视科技,神思电子,中安云科,北京凝思软件)

a) 一个整型数:· int a;

b) 一个指向整型数的指针: int *a;

c) 一个指向指针的的指针,它指向的指针是指向一个整型数: int **a;

d) 一个有10个整型数的数组: int a[10];

e) 一个有 10个指针的数组,该指针是指向一个整型数的 int *a[10];

f) 一个指向有 10个整型数数组的指针: int (*a)[10];

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数:

int (*p)(int)

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

int (*arr[10])(int)

函数

1. 定义

一个完成特定功能的代码模块

2. 三要素

功能、参数、返回值

3. 格式

存储类型 数据类型 函数名(参数列表)

{

函数体;

}

1) 没有参数:参数列表可以省略,也可以用void

2) 没有返回值:数据类型为 void , 函数内部没有return语句。

3) 有返回值:要根据返回值的数据类型定义函数的数据类型

4) 定义子函数时直接定义在主函数上面,如果定义在主函数下面需要提前声明函数

4. 函数声明

数据类型 函数名(参数列表); // 形参

5. 函数调用

1) 没有返回值:直接调用:函数名(实参);

2) 有返回值:如果需要接收返回值,就要定义一个与返回值类型相同的变量去接收

如果不需要接收返回值,就直接调用函数

#include <stdio.h>

void fun()
{
    printf("hello\n");
}

void add1(int a, int b)
{
    printf("sum = %d\n", a + b);
}

int add2(int a, int b)
{
    return a + b;
}

int sub(int a, int b);

int main(int argc, char const *argv[])
{
    fun();
    add1(1, 2);
    int sum = add2(3, 5);
    printf("%d\n", sum);
    int su7 = sub(7, 3);
    printf("%d\n", su7);

    return 0;
}

int sub(int a, int b)
{
    return a - b;
}

练习1:编写一个函数,函数的2个参数,第一个是一个字符,第二个是一个char *,返回字符串中该字符的个数。

#include <stdio.h>

int count(char c, char *s)
{
    int n = 0;
    while (*s != '\0')
    {
        if(c == *s)
        {
            n++;
        }
        s++;
    }
    return n;
}

int main(int argc, char const *argv[])
{
    printf("请输入一个需要查找的字符:");
    char c = getchar();
    char str[32];
    printf("请输入一个字符串:");
    scanf("%s", str);
    int num = count(c, str);
    printf("查找到该字符个数为:%d个\n", num);

    return 0;
}

练习2:编程实现strlen函数的功能,strlen计算字符串实际长度,不包含’\0’

6. 函数传参

6.1. 值传递

单向传递,将实参传递给形参使用,改变形参实参不会受到影响

#include <stdio.h>

int fun(int num, int sum)
{
    num++;
    sum++;
    return num + sum;
}

int main(int argc, char const *argv[])
{
    int a = 3, b = 4;
    int ret = fun(a, b);
    printf("%d %d %d\n", a, b, ret);  // 3 4 9
    return 0;
}
6.2. 地址传递

双向传递,在函数中修改形参,实参也会随之变化

#include <stdio.h>

int fun(int *num, int *sum)
{
    (*num)++;
    (*sum)++;
    return *num + *sum;
}

int main(int argc, char const *argv[])
{
    int a = 3, b = 4;
    int ret = fun(&a, &b);
    printf("%d %d %d\n", a, b, ret);  // 4 5 9
    return 0;
}
6.3. 数组传递

和地址传递一样,参数中存在数组的定义,它也会认为是一个指针

#include <stdio.h>

char *fun2(char a[32])
{
    a = "hello";
    
    char *str = "hello";
    // 如果是数组的话是 32, 如果是指针的话是4
    printf("%d\n", sizeof(a));  // 4
    return a;
}

int main(int argc, char const *argv[])
{
    char *ch = fun2("abc");
    printf("%s\n", ch);
    
    char *str = "hello";
    return 0;
}

补充:

char *p = "hello";
// p 在栈区开辟4字节空间存放字符串常量“hello”的首地址
// “hello“:存放在常量区 // 所以是不能被修改的

char buf[32]="hello";
//buf:在栈区开辟32字节空间,存放"hello"字符串

// 所以当时说怎么去存放字符串,使用字符串数组,可以遍历修改

char * GetMemory(void)

{

char p[] = "hello world";

return p;

}

void Test(void)

{

char *str=NULL;

str = GetMemory();

printf(str);

}

请问运行 Test 函数会有什么样的结果?

答:编译时会报警告,提示局部变量;运行结果不同的编译器环境下,可能会有不同的结果,可能会出现打印乱码或正常输出;也有编译器会出现段错误

练习:

2. a=3,b=5,对a和b的值进行交换。 (北京君正集成电路)

3. 写一个函数判断某一年是不是闰年

动态内存开辟(开辟堆区空间)

为什么存在动态内存开辟

<1>在技术方面,普通的空间申请,都是在全局或者栈区,全局一般不太建议大量使用,而栈空间有限,那么如果一个应 用需要大量的内存空间的时候,需要通过申请堆空间来支持基本业务。

<2>在应用方面,程序员很难一次预估好自己总共需要花费多大的空间。想想之前我们定义的所有数组,因为其语法约束,我们必须得明确"指出"空间大小.但是如果用动态内存申请(malloc)因为malloc是函数,而函数就可以传参,也就意味着,我们可以通过具体的情况,对需要的内存大小进行动态计算,进而在传参申请,提供了很大的灵活性

开辟空间

#include <stdlib.h>

void *malloc(size_t size);

功能:在堆区开辟空间

参数:size:开辟空间的大小 (单位:字节)

返回值:

成功:返回开辟空间的首地址

失败:NULL

释放空间

#include <stdlib.h>

void free(void *ptr);

功能:释放堆区空间

参数:ptr:堆区空间的首地址

返回值:无

free(p);

// 对p进行释放的时候需要对 p 赋值一个NULL,避免它成为野指针

p = NULL;

例子:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{  
    // 为开辟n个数据类型的大小
    int n;  
    scanf("%d", &n);
    int *p = (int *)malloc(n*sizeof(int));
    // 容错判断
    if(p == NULL)
    {
        printf("开辟失败\n");
        return -1;
    }
    printf("开辟成功\n");
    // 使用堆区空间
    for (int i = 0; i < n; i++)
    {
        *(p+i) = i;
    }
    // 释放堆区空间
    free(p);
    p = NULL;
    
    return 0;
}

注意:
1.手动开辟堆区空间,要注意内存泄漏
	当指针指向开辟堆区空间后,又对指针重新赋值,则没有指针指向开辟带队去空间,就会造成内存泄漏
2.使用完堆区空间后及时释放空间

思考:如下代码的输出结果

char * fun(char *p)//p=NULL
{
    p =  "hello";
    // 这个就是把 hello 赋值到 p 里面去了
    return p;
}
main()
{
    char *m = NULL;
    m = fun("abc");
    printf("%s\n", m);
}
代码出现段错误,原因?

解决方案:

总结 如果在子函数中开辟堆区空间,想在主函数中拿到堆区空间首地址有两种方法:

a. 通过返回值
char * fun()
{
	char *p = (char *)malloc(32);
	strcpy(p, "hello");	
	return p;
}
int main()
{
  // 定义一个指针接收返回值
	char *m = fun();
	printf("%s\n",m);
	free(m);
	m = NULL;
	return 0;
}
b. 通过传参
void fun(char **p) // &m
{
	*p = (char *)malloc(32); // m
	strcpy(*p, "hello");
}
int main()
{
	char *m = NULL;
	fun(&m);
	printf("%s\n", m);
	free(m);
	m=NULL;
	return 0;
}

string 函数族

1. strcpy

#include <string.h>

char *strcpy(char *dest, const char *src);

功能:实现字符串的复制

参数:dest:目标字符串首地址

src:源字符串首地址

返回值:目标字符串首地址

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char s[32];
    char a[32] = "hello";
    strcpy(s, a);
    printf("%s\n", s);
    return 0;
}

复制包括\0

char *strncpy(char *dest, const char *src, size_t n);

功能:实现字符串的复制

参数:dest:目标字符串首地址

src:源字符串首地址

n:字符个数

返回值:目标字符串首地址

复制src前n个字符

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char s[32] = "world";
    char a[32] = "hello";
    strncpy(s, a, 2);
    printf("%s\n", s);
    return 0;
}

2. strlen

#include <string.h>

size_t strlen(const char *s);

功能:计算字符串的实际长度

参数:s:字符串的首地址

返回值:实际长度

3. strcat

#include <string.h>

char *strcat(char *dest, const char *src);

功能:用于字符串拼接

参数:dest:目标字符串首地址

src:源字符串首地址

返回值:目标字符串首地址

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char s[32] = "hello";
    char a[32] = "world";
    strcat(s, a);
    printf("%s\n", s);
    return 0;
}

char *strncat(char *dest, const char *src, size_t n); 拼接src的前n个字符

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char s[32] = "hello";
    char a[32] = "world";
    strncat(s, a, 2);
    printf("%s\n", s);
    return 0;
}

4. strcmp

#include <string.h>

int strcmp(const char *s1, const char *s2);

功能:用与字符串比较

参数:s1、s2 用于比较字符串的首地址

返回值:

从字符串首个字符开始比较字符的 ASCII的大小,如果相等继续向后比较

1 s1 > s2

0 s1 == s2

-1 s1 < s2

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char s1[] = "hello";
    char s2[] = "hellohello";
    char *s3 = "nihao";
    char *s4 = "shijie";
    char *s5 = "hello";
    int ret  = strcmp(s1, s2);  // s1 < s2 = -1
    int ret1 = strcmp(s1, s3);  // s1 < s3 = -1
    int ret2 = strcmp(s2, s4);  // s2 < s4 = -1
    int ret3 = strcmp(s3, s2);  // s3 > s2 = 1
    int ret4 = strcmp(s1, s5);  // s1 == s5 = 0

    if(!strcmp(s1, s5))   !false == true
    {
        printf("相等\n");
    }
    return 0;
}

int strncmp(const char *s1, const char *s2, size_t n); 比较前n个字符的大小

递归函数

1. 定义:自己调用自己

2. 执行过程分为两个阶段

a. 递推阶段:从原问题出发,按递推公式从未知到已知最终达成递归的终止条件

b. 回归阶段:按递归的终止条件求出结果,逆向逐步带入递归公式,回到原问题求解

3. 递归的两个必要条件

a. 存在限制条件,当满足这个限制条件的时候,递归便不再继续。

b. 每次调用之后越来越接近这个限制条件

4. 大事化小

例子:

求一个5-1的乘积

#include <stdio.h>

int fun(int n)
{
    if(n == 1)
    {
        return 1;
    }
    return n*fun(n-1);
}

int main(int argc, char const *argv[])
{
    printf("%d\n", fun(5));
    return 0;
}

例子:打印一个数的每一位

接收一个整型值,按照顺序打印它的每一位

示例:1234

输出:1 2 3 4

思路:按照顺序打印它的每一位我们就用 1234%10就会等于 4,想打印其他的数1234怎么来呢,1234/10 = 123,再继续 123%10等于3,以此类推

#include <stdio.h>

void fun(int n)
{
    if(n > 9)
    {
        fun(n / 10);
    }
    printf("%d\n", n % 10);
}

int main(int argc, char const *argv[])
{
    fun(1234);
    return 0;
}

标签:return,int,基础,笔记,C语言,char,地址,printf,hello
From: https://blog.csdn.net/weixin_58298518/article/details/140016253

相关文章

  • C语言两个较大数字相加
    C语言两个较大数字相加思路分析由于C语言中的基本数据类型(如int、long等)有固定的大小,无法直接处理非常大的数字(如数百位的数字)。因此,我们需要采用字符串或数组来表示大数字,并逐位进行加法操作。具体思路如下:输入处理:将两个大数字以字符串的形式输入,并将其反转,以方便从低......
  • C语言笔记(第n版):数据类型与运算
            尽管对于计算机而言无所谓数据类型,因为所有的数据都在计算机中以二进制数进行存储,运输和计算,但是对数据进行人为的划定有益于人们对于数据的操作。        在C语言中对于数据类型的划分(因人而异)大致为:一、基本数据类型 什么是数据类型?   ......
  • 【机器学习算法基础】(基础机器学习课程)-07-朴素贝叶斯算法-笔记
    一、朴素贝叶斯算法原理        朴素贝叶斯(NaiveBayes)是一种基于贝叶斯定理的简单而强大的分类算法,尤其适用于文本分类问题,如垃圾邮件检测、情感分析等            二、朴素贝叶斯算法对新闻进行分类案例  1.数据准备假设......
  • 木舟0基础学习Java的第九天
    面向对象OOPfinal(最终的):用final修饰的所以变量名必须大写修饰类:类不能被继承修饰变量:变量就变成了常量只能被赋值一次修饰方法:方法不能被重写多态(polymorphic)多态的前提:         1.有继承关系         2.有方法重写//在多态中编......
  • Python学习笔记27:进阶篇(十六)常见标准库使用之质量控制中的代码质量与风格第一部分
    前言本文是根据python官方教程中标准库模块的介绍,自己查询资料并整理,编写代码示例做出的学习笔记。根据模块知识,一次讲解单个或者多个模块的内容。教程链接:https://docs.python.org/zh-cn/3/tutorial/index.html质量控制质量控制(QualityControl,QC),主要关注于提高......
  • Vue3实战笔记(64)—Vue 3自定义指令的艺术:实战中的最佳实践
    文章目录前言一、一些简单的Vue3自定义指令超实用案例总结前言书接上文,在Vue3中,自定义指令是一种强大的工具,允许我们扩展HTML元素的功能。通过自定义指令,我们可以创建可重用的行为,并将它们绑定到任何元素上。下面,本文备份一些简单的Vue3自定义指令超实用案例,并解释......
  • 《智能计算系统》第五章 编程框架原理(上)课程笔记
    《智能计算系统》第五章编程框架原理(上)课程视频链接:https://www.bilibili.com/video/BV1Ei421i7Rg本文源自于B站国科大计算所智能计算系统课程官方账号所公开上传的视频,在原有视频之上,提取了关键帧、将音频转成了文字并进行了校正,以便学习使用。在此,也感谢国科大计算所智能......
  • Web安全基础学习:Python反序列化漏洞之pickle反序列化
    理论基础序列化与反序列化序列化和反序列化是指用于将对象或数据结构转换为字节流的过程,以便在不同系统之间进行传输或存储,并在需要时重新构造。序列化是指将对象或数据结构转换为字节流的过程。在序列化过程中,对象的状态和数据被转换为一系列字节,这些字节可以按照一定......
  • C语言命名规范
    C语言命名规范在C语言中,命名规范对于代码的可读性和可维护性至关重要。以下是一些常见的C语言命名规律和建议变量命名变量名应该具有描述性,清晰地表达变量的用途或含义。变量名使用小写字母和下划线(snake_case)的组合,例如intmy_variable;。避免使用单个字符作为变量名,除非......
  • 学习笔记485—Excel技巧:一键将文本数字转换为数值
    Excel技巧:一键将文本数字转换为数值在使用Excel进行数据处理时,经常会遇到数据格式不匹配的问题。特别是当从外部导入数据或手动输入数据时,数字可能会被误识别为文本格式,这在进行数据计算和分析时会带来诸多不便。幸运的是,Excel提供了一些便捷的方法,可以帮助我们一键将文本转换为......