简体   繁体   English

C中的指针问题? 对输入进行排序的函数,作为字符串返回并在 main 中打印

[英]pointer problems in C? function to order inputs, return as string and print in main

I want to write a function that will take two integers, three times, and then return them ordered by the first integer and (for now) print them in main (though eventually I plan/hope to switch to a file-based structure to store and organize data), but I think I might have an issue with my pointers cause even when I skip concatenations (which looks like might also be another separate issue), everything Ive tried has main print a string (or no string) which never matches the input, but the print statements suggest all the looped assignments are working properly.我想编写一个函数,它将接受两个整数,三次,然后按第一个整数排序返回它们,并(现在)在 main 中打印它们(尽管最终我计划/希望切换到基于文件的结构来存储并组织数据),但我认为我的指针可能有问题,即使我跳过连接(这看起来也可能是另一个单独的问题),我尝试过的所有内容都主要打印一个永远不匹配的字符串(或没有字符串)输入,但打印语句表明所有循环分配都正常工作。

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

const char * entry()
{
    int n;
    int level;
    char habit1entry[6];
    char habit2entry[6];
    char habit3entry[6];
    for (int c = 0; c< 3; c++){
        printf("Habit #\n");
        scanf("%d", &n);
        printf("Level:\n");
        scanf("%d", &level);
        switch (n)
        {
            case 1:;
                sprintf(habit1entry, "|%d|%d|\n", n,level);
                printf("n = %d\n",n); 
                printf("%s\n",habit1entry);
                continue;
            case 2:;
                sprintf(habit2entry, "|%d|%d|\n", n,level);
                printf("n = %d\n",n);
                printf("%s\n",habit2entry);
                continue;
            case 3:;
                sprintf(habit3entry, "|%d|%d|\n", n,level);
                printf("n = %d\n",n);
                printf("%s\n",habit3entry);
                continue;
        }
    }
    strcat(habit2entry,habit3entry);
    printf("%s\n",habit2entry);
    strcat(habit1entry,habit2entry);
    printf("%s\n",habit1entry);
    char *fullEntry=habit3entry;
    printf("%s\n",fullEntry);

    return strdup(&fullEntry[0]);
}
int main(){
    const char * dataEntry = entry();
    //strcpy(dataEntry,entry());
    printf("Data:\n%s",dataEntry);
}

heres an example of the output(after the correct prints inside the switch cases) for an input of 3 2 1 1 2 2: "这是输入 3 2 1 1 2 2 的输出示例(在开关盒内正确打印之后):“

|2|2| |2|2|

|1|1| |1|1|
|2|2| |2|2|
|2|2| |2|2|
| |
|2|2| |2|2|
| |
* stack smashing detected * : ./a.out terminated * 检测到堆栈粉碎 * : ./a.out 终止
Aborted (core dumped) "中止(核心转储)”

ps Sorry if this all sounds silly, this is my first C project (and first real stack overflow post, plz b gentl) coming from jumping around between java, python and clojure and I would like to take an operating systems class that allows you to start without knowing C but expects you to pick it up on your own and its hard finding material that explains C concepts in a scope that matches my background knowledge and current learning constraints in terms of time available for taking deep dives through explanations that for me have ended up mostly being either hopelessly esoteric, incredibly case-specific or overly-simplistic/redundant/unhelpful explanations of programming concepts I picked up in other languages. ps 抱歉,如果这一切听起来很傻,这是我的第一个 C 项目(也是第一个真正的堆栈溢出帖子,请务必在 java、python 和 clojure 之间跳来跳去),我想学习一个操作系统类,它可以让你从不了解 C 的情况下开始,但希望您自己掌握它及其难以找到的材料,这些材料可以在与我的背景知识和当前学习限制相匹配的范围内通过对我的解释进行深入研究的时间范围内解释 C 概念对我在其他语言中学到的编程概念的解释大多是极其深奥的、令人难以置信的特定案例或过于简单/冗余/无用的解释。 Dont mean to complain or harp on and its probably good to get practice with different methods of asking questions and finding answers for problems like these, but the learning curve for understanding things like this (setting up the compiler/json files involved spending hours only to discover that mcafee was deleting my exes which I became convinced was a symptom of a virus, only to have the behavior stop after I restarted for a minor routine windows update and I have no idea why) outside of a traditional framework sometimes seems more like a wall and I'm worried that maybe I should revise my approach to avoid wasting too much of my time banging my head against a series of very sturdy walls.不要抱怨或抱怨,练习使用不同的提问和寻找此类问题的答案的方法可能是件好事,但是理解此类事情的学习曲线(设置编译器/json 文件只需要花费数小时发现 mcafee 正在删除我的前任,我确信这是病毒的症状,只是在我重新启动一个次要的例行 Windows 更新后行为停止了,我不知道为什么)在传统框架之外有时看起来更像是一个墙,我担心也许我应该修改我的方法以避免浪费太多时间将我的头撞到一系列非常坚固的墙壁上。 any and all advice is greatly appreciated.非常感谢任何和所有建议。

Abstracting form the logic of the program, you have plenty of issues there:抽象形式的程序逻辑,你有很多问题:

  1. You do not provide enough space for the strings您没有为字符串提供足够的空间
  2. Your switch is not very related to your for loop您的开关与您的 for 循环关系不大
  3. Names of the variables do not matter for you - but they matter for the program .变量的名称对您来说无关紧要,但对程序来说却很重要。 Be more careful.多加小心。
  4. probably more but I forgot already可能更多,但我已经忘记了
#include <stdio.h>
#include <string.h>

const char * entry()
{
    int n;
    int level;
    char habit1entry[21] = "";
    char habit2entry[14] = "";
    char habit3entry[7] = "";
    for (int c = 1; c < 4; c++){
        printf("Habit #\n");
        scanf("%d", &n);
        printf("Level:\n");
        scanf("%d", &level);
        switch (c)
        {
            case 1:;
                sprintf(habit1entry, "|%d|%d|\n", n,level % 10);
                printf("n = %d\n",n); 
                printf("He1: %s\n",habit1entry);
                continue;
            case 2:;
                sprintf(habit2entry, "|%d|%d|\n", n,level % 10);
                printf("n = %d\n",n);
                printf("He2 = %s\n",habit2entry);
                continue;
            case 3:;
                sprintf(habit3entry, "|%d|%d|\n", n,level % 10);
                printf("n = %d\n",n);
                printf("He3 = %s\n",habit3entry);
                continue;
        }
    }
    strcat(habit2entry,habit3entry);
    printf("H2 + H3 = %s\n",habit2entry);
    strcat(habit1entry,habit2entry);
    printf("H1 + H2 = %s\n",habit1entry);
    char *fullEntry=habit1entry;
    printf("FE: %s\n",fullEntry);

    return strdup(fullEntry);
}
int main(){
    const char * dataEntry = entry();
    //strcpy(dataEntry,entry());
    printf("Data:\n%s",dataEntry);
}

Welcome to the weird and wonderful world of C.欢迎来到 C 的奇异而奇妙的世界。

I have not actually compiled and run your program yet, just had a quick read through and though I give you my first thoughts.我还没有真正编译和运行你的程序,只是快速通读了一遍,尽管我给了你我的第一个想法。

The way your program is written is primed to generate stack overflows.编写程序的方式已准备好产生堆栈溢出。 You have three (very little) character arrays defined on the stack habitxentry, so your sprintf's will most certainly blow your stack unless both the Habit and Level inputs are less than 10. Habit is alright because your switch only allows 1, 2 or 3. Your switch does nothing if Habit is anything else.您在堆栈惯用条目上定义了三个(非常少)字符数组,因此您的 sprintf 肯定会炸毁您的堆栈,除非习惯和级别输入都小于 10。习惯没问题,因为您的开关只允许 1、2 或 3。如果习惯是别的什么,你的开关就什么都不做。

As a side note: sprintf is not really the function to use in our security minded world.附带说明: sprintf 并不是在我们注重安全的世界中真正使用的功能。 snprintf is a better choice. snprintf 是更好的选择。 Not really an issue here per se as you are not passing in user supplied data but still, it's not a good habit to cultivate.这里本身并不是一个真正的问题,因为您没有传递用户提供的数据,但这仍然不是一个好习惯。

Next you strcat your character arrays together, virtually guarantying a stack violation, but lets assume this works;接下来,您将字符数组 strcat 在一起,实际上保证了堆栈违规,但让我们假设这是有效的; you are concatenating 2 and 3 into habit2entry and then 1 and 2 into habit1entry.您将 2 和 3 连接成习惯 2 条目,然后将 1 和 2 连接成习惯 1 条目。

Next you are creating a pointer to habit3entry (not habit1entry) and returning a duplicate.接下来,您将创建一个指向习惯 3 条目(而不是习惯 1 条目)的指针并返回一个副本。

By doing so you are allocating heap in a mildy obscure manner.通过这样做,您正在以一种温和晦涩的方式分配堆。 The callee will be responsible for freeing this memory.被调用者将负责释放此内存。 I always preferred to explicitly malloc the memory and then strcpy (or memcpy) the data in. Now when you grep your code, you only have to look for malloc.我总是喜欢显式地 malloc 内存,然后 strcpy(或 memcpy)数据。现在当你 grep 代码时,你只需要寻找 malloc。 Also, someone using the function will notice the malloc, see you have returned to pointer and realize that freeing it will now be his problem.此外,使用该函数的人会注意到 malloc,看到您已返回指针并意识到释放它现在将是他的问题。

In order to avoid these problems some programmers leave it to the caller to supply a buffer to the function.为了避免这些问题,一些程序员让调用者为函数提供缓冲区。 The reasoning is that a function is supposed to do one thing and one thing only.推理是一个函数应该做一件事并且只做一件事。 In this case you are doing two things, you allocate memory and you fill that memory.在这种情况下,您正在做两件事,分配内存并填充该内存。

In your switch statement I noticed that each of your case labels are followed by an empty statement.在您的 switch 语句中,我注意到您的每个 case 标签后跟一个空语句。 the semicolon at the end of that line is not necessary: write "case 1:" not "case 1:;"该行末尾的分号不是必需的:写“case 1:”而不是“case 1:;” You also use continue at the end of each block.您还可以在每个块的末尾使用 continue。 This is allowed but "break" is more appropriate.这是允许的,但“中断”更合适。 In this case it will have the same effect but normally you have more statements after the switch.在这种情况下,它将具有相同的效果,但通常在 switch 之后会有更多的语句。 Now the difference will become apparent.现在差异将变得明显。 Continue will jump straight to the top of the loop, break will break out of the switch and continue executing there. Continue 会直接跳到循环的顶部,break 会跳出 switch 并在那里继续执行。

Hope this gives you some insight.希望这能给你一些见解。

Good luck.祝你好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM