黑马程序员_IOS开发_Objective-C学习笔记_分析和调试自己的一个黑马基础测试题程序

1.程序

1.1程序介绍

从键盘输入6个字符串(仅仅包含英文字母和数字),对着6个字符串从小到大排列并输出结果。(C语言)

1.2程序设计分析

这个是我在基础测试题目环节从黑马报名系统里面下得题目。

初看题目,觉得此题不难,无非是:

*建立一个字符串的数组

*然后在一个循环6次的循环体里面循环输入6次,然后再把输入的字符串放到字符串数组里面

*然后建立一个长度数组来统计相对应的字符串的长度

*最后对长度数组中的元素按大小来排序(注意:对长度数组中的元素排序的时候,同时要对字符串数组进行一样的操作,方便后面输出字符串

*按数组顺序输出字符串数组中的元素

1.3第一版本的程序如下

/**
 *9.从键盘输入6个字符串(仅仅包含英文字母和数字),对这6个字符串从小到大排列并输出结果。(C语言)
 */

#include <stdio.h>
int main(int argc, const char * argv[])
{
  //1.新建一个字符串变量用于保存每次输入的字符串
    char* string;
    //2.新建一个字符串数组,保存各个字符串的地址
    char* stringGroup[6];

    //3.定义一个变量保存字符串的长度
    unsigned long length[6];

    //4.系统提示用户连续输入6个字符串 回车确定
    printf("请依次输入6个字符串,包含数字和字母,回车确定\n");

    //5.循环提示用户 连续输入字符串
    for(int i = 0;i < 6;i++)
    {

        //5.1提示用户输入第N个字符串
        printf("请输入第%d个字符串\n",(i+1));

        //5.2保存第n个字符串到字符串指针数组的n项
        scanf("%s",string);

        //5.3获取相应的字符串的长度并一次保存到长度数组
        length[i] = strlen(string);

     //5.4string赋值给stringGroup数组
     stringGroup[i] = string;

    }

    //6.新建2个变量 用作交换操作的中间变量
    char *pSwap;
    unsigned long swap;

    //7.冒泡排序
    for(int i = 0;i < 6;i++)
    {
        for(int j = i+1;j < 6;j++)
        {
            //7.1当长度数组当前元素大于后面个元素的时候
            if(length[i] > length[j])
            {
                //7.1.1交换相应索引的长度数组的值
                swap = length[i];
                length[i] = length[j];
                length[j] = swap;

                //7.1.2交换相应索引的字符串数组的值
                pSwap = stringGroup[i];
                stringGroup[i] = stringGroup[j];
                stringGroup[j] = pSwap;
            }

        }
    }

    //8.循环打印出字符串数组
    for(int i = 0;i < 6;i++)
    {
        printf("%s  ",stringGroup[i]);
    }
    return 0;
}

1.4程序运行错误结果

结果程序输出大失所望,连续输出最后一次输入的字符串6遍。

例如:最后一次我输入的字符串是“itheima”,则程序运行结果如下:

itheima  itheima  itheima  itheima  itheima  itheima

2.程序调试

2.1程序错误分析

首先,这个程序没有直接显示错误或者警告,也能运行,说明程序的问题在于算法和逻辑。

于是我采用在主程序最开始的地方加了一个断点一步一步跟踪程序看能有什么发现。

果然,在输入循环第二次的时候 我发现对string的输入字符串,stringGroup同样会变化,代码如下:

    for(int i = 0;i < 6;i++)
    {  //这个时候i==1,已经是第二次循环了
        printf("请输入第%d个字符串\n",(i+1));
        //第二次字符串输入,例如:第一次我输入的是itheima,第二次我输入的是itcast
        scanf("%s",string);
        length[i] = strlen(string);
     //这时我们会在Xcode下方左边的变量监视窗口神奇的发现stringGroup数组的第一的元素“itheima” 神奇的变为“itcast” 与最近输入的一个字符串一样
     stringGroup[i] = string;
    }

2.2程序错误判断

经过分析,我发现了错误的原因。每次循环体string赋值的时候实际上是把string字符串的指针赋值给了stringGroup数组里面,也就是说

连续六次的输入都是string指针赋值到stringGroup数组里面,所以stringGroup数组里全是string指向的字符串。

所以当我们最后一次输入string字符串后,stringGroup数组保留的是6个一样的string字符串。

2.3程序第一次修改

这次我把字符串变量string定义的位置放到输入循环体里面,修改后的代码如下:

    for(int i = 0;i < 6;i++)
    {  
        char *string;
        //这个时候i==1,已经是第二次循环了
        printf("请输入第%d个字符串\n",(i+1));
        //第二次字符串输入,例如:第一次我输入的是itheima,第二次我输入的是itcast
        scanf("%s",string);
        length[i] = strlen(string);

     //这时我们会在Xcode下方左边的变量监视窗口神奇的发现stringGroup数组的第一的元素“itheima” 神奇的变为“itcast” 与最近输入的一个字符串一样
     stringGroup[i] = string;
    }

结果还是一样的问题,那么问题出在哪里呢?

2.4程序第二次修改

于是我接着第二次跟踪调试,发现问题还是出现在string变量定义上面,我原以为每次循环后string变量会释放 然后重新分配。

但是实际上因为我们采用的是静态定义的方法 char *string,所以在for循环的{}后,string不会每次循环都被定义一次 然后被分配一次新的内存空间。

原因找到了,怎么解决这个问题呢?

*用C语言自身的相关的动态分配内存功能来操作,每次输入循环,调用内存分配一次新的空间来接受输入到的字符串,然后赋值给stringGroup,最后在程序结束的时候统一release。

*用OC里面的NSString,因为每次都是在堆里面动态分配内存 alloc,所以也能得到一样的效果,不过出于本题是要用C语言来解决,所以就没有用NSString了。

最终解决方案如下:

既然只是需要开辟6个字符串的内存空间,那么我就用最暴力的办法在程序开头定义6个字符串变量,然后赋值初始化stringGroup数组好了。

最后完整程序如下:

/**
 *9.从键盘输入6个字符串(仅仅包含英文字母和数字),对这6个字符串从小到大排列并输出结果。(C语言)
 */

#include <stdio.h>
int main(int argc, const char * argv[])
{
    //1.从内存中开辟6个100长的字符串空间
    char string1[100];
    char string2[100];
    char string3[100];
    char string4[100];
    char string5[100];
    char string6[100];

    //2.新建一个字符串数组,保存各个字符串的地址
    char* stringGroup[6]={string1,string2,string3,string4,string5,string6};

    //3.定义一个变量保存字符串的长度
    unsigned long length[6];

    //4.系统提示用户连续输入6个字符串 回车确定
    printf("请依次输入6个字符串,包含数字和字母,回车确定\n");

    //5.循环提示用户 连续输入字符串
    for(int i = 0;i < 6;i++)
    {

        //5.1提示用户输入第N个字符串
        printf("请输入第%d个字符串\n",(i+1));

        //5.2保存第n个字符串到字符串指针数组的n项
        scanf("%s",stringGroup[i]);

        //5.3获取相应的字符串的长度并一次保存到长度数组
        length[i] = strlen(stringGroup[i]);

    }

    //6.新建2个变量 用作交换操作的中间变量
    char *pSwap;
    unsigned long swap;

    //7.冒泡排序
    for(int i = 0;i < 6;i++)
    {
        for(int j = i+1;j < 6;j++)
        {
            //7.1当长度数组当前元素大于后面个元素的时候
            if(length[i] > length[j])
            {
                //7.1.1交换相应索引的长度数组的值
                swap = length[i];
                length[i] = length[j];
                length[j] = swap;

                //7.1.2交换相应索引的字符串数组的值
                pSwap = stringGroup[i];
                stringGroup[i] = stringGroup[j];
                stringGroup[j] = pSwap;
            }

        }
    }

    //8.循环打印出字符串数组
    for(int i = 0;i < 6;i++)
    {
        printf("%s  ",stringGroup[i]);
    }
    return 0;
}

3.总结

*字符串数组保存的只是每个字符串元素的起始地址,并没有镜像新的内存空间。

*如果是定义一个比较大的字符串数组,例如 char* stringGroup[1000] 那么我这种方式就不科学了,只能用一个循环体,每次在循环体里面new新的字符串。

*如果发现程序的算法和逻辑有错误,那么用单步调试外加变量值监视的方法就能最好的找出问题的缘由。

*指针知识点果然是C及其OC的重点和难点,也是C语言家族的特色,必须要充分理解和掌握。

时间: 2024-08-10 01:48:29

黑马程序员_IOS开发_Objective-C学习笔记_分析和调试自己的一个黑马基础测试题程序的相关文章

黑马程序员_JAVA UDP网络编程学习笔记

一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的,它们构成了两个端点之间的虚拟通信链路.与TCP通信不同,UDP是面向无连接的.不可靠的基于数据包的传输协议.即应用进程(或程序)在使用UDP协议之前,不必先建立连接.自然,发送数据结束时也没有连接需要释放.因此,减少了开销和发送数据之前的延时.UDP也采用端口来区分进程. 在java中,java.

黑马程序员_IOS开发_Objective-C学习笔记_类(对象)

1.面向对象编程基本介绍: 面向对象程序设计(英语:Object-oriented programming,缩写:OOP),指一种程序设计范型,同时也是一种程序开发的方法.对象指的是类的集合,类是对象的模板,它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的安全性.重用性.灵活性和扩展性. 2.面向对象与面向过程 面向过程就是分析出解决问题所需要的步骤,然后基于这些步骤用代码和函数把这些步骤一步一步实现. 面向对象是把构成问题事务分解成各个对象,然后各个对象分别对相关的程序功能做出处

黑马程序员_IOS开发_Objective-C学习笔记_基本数据类型

 1.数据类型和常量: 我们在做IOS程序开发的时候使用的最多的恐怕就是基本数据类型,在Objective-c中提供了4种基本的数据类型:int float double以及char. 1.1声明为int的变量只能用于保存整形值. 1.2声明为float类型的变量可存储浮点类型值(即包含小数位数).. 1.3double类型和float类型一样,只不过前者的精度大约是后者的2倍. 1.4最后是char 数据类型,char类型可用来存储单个字符,例如字母a,数字6,或是一个分号.或者通过char指

黑马程序员_IOS开发_Objective-C学习笔记_NSString

NSString NSString是一个IOS OC开发中经常使用到得一个对象类型.NSString是NSObject(Foundation的基础对象)的子类,所以具有NSObject的所有特性. NS是Cocoa类对象类型的前缀,来源于乔布斯公司的操作系统的名字 NEXTSTEP 1.创建NSString对象1.1此方法不需要手动释放内存. NSString *aString = @"This is String"; //注意:这里和C语言字符串不一样,需要在头部放一个@符号 1.2

黑马程序员_IOS开发_Objective-C学习笔记_Foundation框架常用的结构体

1.Foundation框架简介 1.1使用Foundation可以: *创建和管理集合,比如数组和字典 *访问存储在应用程序里的图片和其它资源 *创建和管理字符串 *提交和接收通知 *创建日期和时间对象 *自动发现IP网络上的设备 *操作URL流 *执行异步代码 注意:Foundation框架是默认导入的 所以不用刻意去导入 是必备的框架之一 1.2Foundation框架中一些经常接触到的结构体 由于Objective-C程序中 对象的结构体成员变量中的元素 不能通过对象直接来访问 所以我们

黑马程序员_IOS开发_Objective-C学习笔记_继承和分类

1.什么是继承 继承是面向对象程序的三大特征之一,意思是基于一个类的基础,定义一个新类. 被继承的类成为父类,新类成为子类. 继承的实际例子有许多.例如,一个儿子继承了他父亲,那么他就继承了他父亲的财产. 例如基类是Person,那么我们可以继承Person这个类,产生新的子类Student,Teacher 在这里Person是Student类和Teacher类的父类. 引入继承的概念是有许多好处的: *能够抽取重复代码 *建立了类和类之间的联系 *不改变原来模型的基础上,能够扩展出新的方法 2

黑马程序员_IOS开发_Objective-C学习笔记_指针复习

1.指针 上一篇我在分析我自己写的程序错误里面发现是指针的问题,我觉得有必要在复习一下指针,毕竟指针是C语言系列的难点. 指针是C语言的精髓,但是很多初学者往往对于指针的概念并不深刻,以至于学完之后随着时间的推移对指针的概念和使用越加模糊,感觉指针难以掌握,今天我就再回顾下指针的概念和使用. 2.什么是指针 指针的本质是存放变量地址的变量,简单的说变量p中存储的是变量a的地址,那么p就可以称为是变量a的指针,或者说p指向a.当我们访问a变量的时候其实是程序先根据a取得a对应的地址,再到这个地址对

黑马程序员_IOS开发_Objective-C学习笔记_内存管理

1.内存管理概述 1.1什么是内存管理:内存管理是程序设计中常用的资源管理的一部分,每个计算机系统可供程序使用的内存都是有限的. 1.2为什么要使用内存管理:当我们的程序运行结束的时候,操作系统将回收其我们程序占用内存.但是,只要程序还在运行,它就会一直占用内存.如果不进行及时清理不用的内存,内存最终将被耗尽.每个程序都会使用内存,我们必须确保在需要的时候分配内存,而在程序运行结束时释放占用的内存.如果我们只分配而不释放内存,将发生内存泄漏. 1.3引用计数:1.3.1只有当你对一个对象了all

黑马程序员_IOS开发_Objective-C学习笔记_NSArray/NSMutableArray

1.数组简介 NSArray:用来存储有序数组,它是不可变的(不能插入删除数据元素),也不能存储C语言中的基本数据类型(int.float.double.enum.struct),也不能存nil,如果需要使用基本数据类型,需要先转成Objective-c中的对象,用@包装下. NSMutableArray:它是可变的(可以插入删除数据元素),还能限制元素个数 capacity.当你需要对数组元素进行加减操作的时候,就需要使用NSMutableArray. 2.创建数组 //1.array NSA