I'm creating a program to open .txt files in a given directory, I have an array with all the absolute paths of the files inside the directory in question and I'm creating a function to extract and return the name of the files, the function is written as follows:
char *name(char *string) {
int i = strlen(string);
char *aux;
while(string[i-1] != '/'){
i--;
}
strcpy(aux, &string[i]);
return aux;
}
The above function is giving a Segmentation Fault error, but if I add the following line " int j = 0;" before the declaration of aux the mistake is gone, the new and working code is
char *name(char *string) {
int i = strlen(string);
int j = 0;
char *aux;
while(string[i-1] != '/'){
i--;
}
strcpy(aux, &string[i]);
return aux;
}
input: C:\test\a.txt
output: a.txt
Why the addition of "int j = 0;" solves the problem? I'm stuck with that and can't continue because I don't know if this inconsistency might lead to bigger problems later, I'm thinking about writing my own function to copy the strings, but before that I really want to understand that error.
You never allocate aux. aux needs to point to a valid memory location before you attempt to copy anything to it.
Instead of char *aux, you need something like char *aux = malloc(i+1);. Note that i+1 is overkill because in your case aux will always be at least 3 characters shorter than string (it won't contain C:\), but you probably don't care for such small strings. Remember to free() the pointer once you're done with it.
Also, the reason you found it works by switching orders of declarations and/or adding a declaration is probably that you got lucky and somehow the location to which aux points to is valid (if you do just char *aux;, aux points to a random location). This is pure luck however, and is still invalid code even though it seems to work.
In the future, you might want to use a tool like Valgrind to help you diagnose memory problems. You should also read a tutorial on basic memory management and pointers in C.
Since it sounds like you are only interested in utilizing the filename portion of the string as parameter, another option is to use the portion of the string you already have.
Try: aux = &string[i]; instead of the strcpy.
This gives you a pointer into the part of the string you are interested in (namely, the last part after the final '/').
Secondly, ensure you have a '/' in all of your input strings, otherwise bad things will happen (i.e. your loop will go past the beginning of the string, likely encountering a segmentation fault at some point). It would be best to put a condition on the loop so that it doesn't continue beyond i = 1.
You don't allocate any memory to aux. You are trying to write to memory through an uninitialized pointer.
Related
While working on a CS50 problem set (substitution), I encountered a segmentation fault when running the code. After some searching I found out that assigning memory (malloc) to string "output" fixes the issue. However I wanted to understand why assigning memory is necessary here?
Any explanation would be appreciated.
code extract: -please note i am using the cs50.h library
string cipher(string input, string key) {
string output=malloc(strlen(input)+1);
for (int i=0, len = strlen(input); i<len; i++) {
if(isalpha(input[i]) != 0) {
output[i] = substitute(input[i], key);
}
else {
output[i] = input[i];
}
}
return output;
free(output);
}
As much as I could know about CS50.h string, I came to know that
string output
is just declaring a character pointer named output. So what actually happens in your code is that until you explicitly declare the "strlen(input)+1" contiguous memory locations beloging to output only, they are essentially free locations for the program. Hence, your output pointer will contain only the character at the 0th index. The function returns what "output" actually is, a pointer. Some process within the program meanwhile may make use of all the other memory locations other than output[0], since they never belonged to output string. output only pointed to the first character of some string.
Bound checking in C, C++ is essentially done by the programmer. output[i] for any arbitrary i will never give an error because it's a simple pointer arithmetic for the programmer, i.e. output[i] = *(output+i).
:)
For starters this statement
free(output);
never gets the control because it is placed after the return statement
return output;
and moreover it does not make a sense. It is the caller of the function that is responsible to free the allocated memory.
You need to dynamically allocated memory because otherwise if you will declare a variable length array like
char output[strlen(input)+1];
then after exiting the function it will not be alive and an attempt to access the array outside the function results in undefined behavior.
That is if you will just write
string output;
that is equivalent to
char *output;
then the pointer output has indeterminate value because it was not initialized and neither memory was allocated where the source string will be copied.
You could change the source string input in place without creating one more array.
In this case it would be enough to write
if(isalpha(input[i]) != 0) {
input[i] = substitute(input[i], key);
}
and then you could place the statement
return input;
Pay attention to that using the alias string for the type char * is a bad idea.
The function declaration if to rewrite it like
char * cipher(char *input, char *key);
is confusing. It is unclear whether the strings input and key are being changed within the function or not.
If you want that the function returns a new string build from the source string then the function declaration should look like
char * cipher(const char *input, const char *key);
Thus answering your question
However I wanted to understand why assigning memory is necessary here?
if you want to create a new string from the source string pointed to by the pointer input then it is evident that you need to allocate a new character array where elements of the source string will be copied.
Otherwise if you want to change the source string in place then there is no need to create one more array.
I need a function that receives an array of pointers to strings and it's size.
Then it should seek for those strings which occur in the array more than once - then I have to delete them and realloc the array.
Function should return new size of the array.
I'm trying to solve this, and not sure what`s wrong.
I want to move each string, that I want to delete, to the end of the array and then delete it, but not sure when the "realloc" should happen.
#include<stdio.h>
#include<stdlib.h>
int DeleteString(char** tab, int n){
char* check=malloc(sizeof(char)*100);
int deleted;
int i,j,g,h;
for(i=0;i<n;i++){
strcpy(check, tab[i]);
for(j=0;j<n;j++){
if(strcmp(check, tab[j]) == 0){
deleted++;
char* temp = malloc(sizeof(char)*100);
for(h=j;h<n-1;h++){
strcpy(temp, tab[h+1]);
strcpy(tab[h+1], check);
strcpy(tab[h], temp);
}
}
if(deleted>0){
realloc(tab, sizeof(char*)*(n-deleted));
}
}
}
return n-deleted;
}
For now there is "Segmentation fault" error
Oops, your code contains numerous problems, because you failed to observe some major rules of C language:
every non static variable shall be initialized (what about deleted?)
any object that was malloced shall be freed (what about check and temp.)
never change something that was passed as an input parameter to a function, or do not expect the change to be visible on return (tab has to be considered here because of next line).
allways assign the result of realloc because it can be different from the input pointer (realloc(tab, sizeof(char*)*(n-deleted));).
The first one is probably the cause of the segmentation fault because as deleted is unitialized its value is just undeterminated. But all problems should be fixed.
The reason for the SEGMENTATION FAULT is tab[0] store the address of the variable which stores the actual string.
Here tab[i] is in for loop,hence when it tries to fetch tab[1] itself memory error.
for(i=0;i<n;i++){
strcpy(check, tab[i]);...}
FOR EXAMPLE:
char *foo = "something";
char **ptr2;
ptr2 = &foo;
printf("check = %s", *ptr2);
for(int i=0;i<9;i++){
printf(" check = %c", ptr2[i]);
}
Output
check = something check = 4check = �check = pcheck =
Actally it is an error.
First of all, don't forget to initialise variables like deleted, as it has been said in other answers.
Next, you are supposed to free memory (as you are deleting items) and you only call malloc(3)). That seems a little counter-common sense, doesn't it?
Third, you make a lot of string copying in the loops, while it should be more efficient just to move pointers up, so you don't need to realloc the string elements and copy the cell contents (by the way, are you sure those strings will be feed to the function as malloc()d strings? I will assume that as you do)
Fourth, consider sorting the array first, so all the similar strings will be adjacent in the array. This has a cost O(n*log(n)) that, appended to the delete next string if equal (with cost O(n)) makes total cost O(n*(log(n)+1)) or O(n*log(n)) and not O(n^2) which is your actual cost)
Once sorted, only the strings deleted should be free(3)d, the pointers moved back to the start of the array as holes get appearing, and finally(when all is finished) you can just realloc(3) the array of pointers (only once, not at every pass through the loop)
Remaking the example is out of the scope of this answer, as it looks actually some school exercise. Sorry for that. I'm sure that the other hints will help you to retry the exercise with more success.
And think: thinking before writing is how one succeeds in this job.
I'm trying to create a dynamic array of 1000 character long strings using calloc:
int i;
char** strarr =(char**)calloc(argc,sizeof(char)*1000);
if(strarr == NULL)
return 0;
strarr[0][0] ='a';
printf("%c\n",strarr[0][0]);
Every time i try to run this code i get segmentation fault on the printf line, i don't get why does this happen (you can assume that argc is bigger than 0)
Thanks
P.s. im sorry that the code is in text format but im using a mobile so i dont have the code feature
Try this:
const int num_of_strings = 255; //argc ?
const int num_of_chars = 1000;
int i;
char** strarr =(char**)malloc(sizeof(char*)*num_of_strings);
if(strarr == NULL)
return 0;
for (i = 0; i < num_of_strings; i++) strarr[i] = (char*)malloc(sizeof(char)*num_of_chars);
Hello and Welcome to the world of undefined behaviour, one of the darkest territories of the C language. Your code has several problems, which cause undefined behaviour in several occasions, but they all get executed, until you reach the printf line, where you are accessing memory, you have not allocated, which is finally caught by your system and, thus, a segmentation fault is produced.
But I think, it would be better to walk ourselves through your code.
The variable i, which is declared in the int i; line is not used anywhere in the code you have posted, but I guess you need it later.
The first piece of code, that is not right, is in this second line, where you declare an array of strings or a char**. That means that you have a pointer to pointers to chars. So, what you really want to do there is allocate memory for those pointers and not for the chars they will point to. Note that a char consumes a different amount of memory than a char*. This line is, thus, the one to go with.
char** strarr = (char**) calloc(argc, sizeof(char*));
This will allocate memory for argc blocks of memory, each of which is of size 4 or 8 bytes, which depends on whether your system is 32 or 64-bit.
You are doing a very good job of checking whether the calloc function returned NULL or not, which is a very good practice overall.
Next, you will want to allocate memory for the strings themselves, that are pointed to by the pointers, for which you allocated memory in the previous line. These lines will do it.
for (int i = 0; i < argc; i++) {
strarr[i] = (char*) calloc(1000, sizeof(char));
}
This will now allocate 1000-character lengthed strings for every element of our argc-sized string array.
After that, you can continue with your code as it is and I think that no errors will be produced. Please accept an additional piece of advice from me. Learn to love valgrind. It is a very helpful program, which you can run your code with, in order to analyse memory. It is my first step, whenever I get a segmentation fault.
Good evening everybody, I am learning C++ on Dev C++ 5.9.2, I am really novice at it. I intentionnally make my programs crash to get a better understanding of bugs. I've just learned that we can pass a char string to a function by initializing a pointer with the address of the array and that was the only way to do it. Therefore we should always pass to the function the size of that string to handle it properly. It also means that any procedure can run with a wrong size passed in the argument line hence I supposed we could read farther than the allocated memory assigned to the string.
But how far can we do it? I've tested several integers and apparently it works fine below 300 bytes but it doesn't for above 1000 (the program displays characters but end up to crash). So my questions are :
How far can we read or write on the string out of its memory range?
Is it called an overflow?
How does the program detect that the procedure is doing something unlegit?
Is it, the console or the code behind 'cout', that conditions the shutting down of the program?
What is the condition for the program to stop?
Does the limit depend on the console or the OS?
I hope my questions don't sound too trivial. Thank you for any answer. Good day.
#include <iostream>
using namespace std;
void change(char str[])
{
str[0] = 'C';
}
void display(char str[], int lim)
{
for(int i = 0; i < lim; i++) cout << str[i];
}
int main ()
{
char mystr[] = "Hello.";
change(mystr);
display(mystr, 300);
system("PAUSE");
return 0;
}
The behavior when you read past the end of an array is undefined. The compiler is free to implement the read operation in whatever way works correctly when you don't read beyond the end of the buffer, and then if you do read too far - well, whatever happens is what happens.
So there are no definite answers to most of your questions. The program could crash as soon as you read 1 byte too far, or you could get random data as you read several megabytes and never crash. If you crash, it could be for any number of reasons - though it likely will have something to do with how memory is managed by the OS.
As an aside, the normal way to let a function know where a string ends is to end it with a null character rather than passing a separate length value.
int readOptions(char *argv[]){
FILE * infile;
char line_buf[BUFSIZ];
int i = 0, j = 0 ;
infile = fopen("options","r");
if(!infile){
fprintf(stderr,"File Read failure\n");
exit(2);
}
while( i < 10 && fgets(line_buf,sizeof(line_buf),infile)!=0){
printf("Line buf : %s",line_buf);
argv[i] = line_buf;
i++;
}
}
int main(){
int j ;
char *options[10];
for(j = 0 ; j< 10 ; j++){
options[j] = malloc(len * sizeof (char));
}
readOptions(options);
for(j=0; j<10 ; j++)
printf("%s %d\n",options[j], j );
}
The problem is that I always see - the program print only the last line read in the file. Where is the mistake ? and am I missing any important pointer concept with this code ?
Every element of argv points to the same line_buf. Use strdup() to create new strings instead (which you will later have to free).
A char* is not a string. It is a pointer. In your loop, you set each char* in your char*[] to point at the beginning of the line_buf array. Thus, each refers to the same data (which, furthermore, is no longer available after returning from the function; you get undefined behaviour at this point and you're just "lucky" - actually very unlucky, because it makes it harder to diagnose the problem - that it seems to "work" as well as it does.)
There is no real string type in C. You must set up separate chunks of memory that will hold the characters, and point at those chunks. If you allocate them dynamically, you will also have to free them. If you want to be able to resize them, or in general handle things of unknown size, that's also on you.
You've written the code to allocate some space, but you don't copy the data into the space - instead, you repoint the pointers at the local buffer. As noted, we use strcpy to copy from one buffer to another. But there's no real point in doing that, if you're going to pass in allocated buffers and limit yourself to their sizes anyway; instead, just fgets directly into the buffers pointed at by the pointers in the argv array, instead of into a local one.
The argv[i]s are not strings in and of themselves, but pointers to areas of memory that contain text strings. That's what you're doing when you say options[j] = malloc(...) -- you're setting aside an area of memory for that pointer to point at. But that area of memory is just an area of memory, it doesn't have anything to do with the pointer, except that the pointer happens to be pointing at it.
So when you say argv[i] = line_buf, that doesn't mean copy the string itself. It means change the pointer, such that argv[i] now points to the same area of memory where line_buf[] starts. Then in the next iteration of your for loop, you overwrite that same area of memory. By the end of the loop, all ten of your pointers are pointing to line_buf, which contains the last line of your file, which is why you get ten copies of that line.
(What's also worth noting is that once readOptions() returns, that area of memory that all your pointers are pointing to is considered undefined, because line_buf[] only "exists" within the readOptions() function. It's only through luck that your program is printing the last line ten times, rather than printing garbage or crashing.)
Now, to copy a string from one place to another, you can use the strcpy() function, which can be added to your program by putting #include <string.h> at the top. You'd then write strcpy(argv[i], line_buf).
Here's what a simple version of strcpy() would look like, so you can see what it's doing:
char *strcpy(char *dest, char *source) {
int i = 0;
while (source[i] != '\0') { /* a zero (or "null") byte means end of string */
dest[i] = source[i];
i=i+1;
}
dest[i] = source[i];
return dest;
}
Notice that strcpy() doesn't have any way of knowing how much space there is to copy into! If you don't have enough space available, it will go right past the end of your space and into who knows what memory areas, which may cause your program to crash or behave strangely. This is called a buffer overrun, and it's one of the most common security errors. So make sure you have enough space before you call strcpy().