I've been trying to figure out what I'm doing wrong in this block of code for a while now and still no luck. According to GDB, I'm receiving a SIGSEV, Segmentation Fault which means that I tried to access an invalid memory address.
Here I'm trying to find the length of the char pointer that is being passed
int getLength(char *start) {
int count = 0;
while(*start) {
count++;
start++;
}
return count;
}
Here is my full C file. I think it might be useful
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int len1 = getLength(argv[1]);
int len2 = getLength(argv[2]);
if (argc != 3) {
fprintf(stderr, "Invalid number of items input.\n");
return 1;
}
if (len1!=len2) {
fprintf(stderr,"From and to are not the same size.\n");
return 1;
}
return 0;
}
int getLength(char *start) {
int count = 0;
while(*start) {
count++;
start++;
}
return count;
}
int duplicatesFrom(char *from) {
int i;
int j;
int len = getLength(from);
for(i=0;i<len;i++) {
for(j=0;j<len;j++) {
if (from[i]==from[j] && i!=j) {
return 1;
}
}
}
return 0;
}
First, if you don't give at least 3 arguments to your program, it will crash since you call getLength with the second and third argument as parameters at the beginning of main().
I suggest you move the two first lines below the argument count check.
Then, as suggested by BathSheba, you shoud consider making the char * passed to getLength() const since getLength() doesn't modify it. It is a good practice and make the code more self-explanatory.
Also, do not forget the forward declaration of the functions you use in main() if their definition are below main(), unless you put the forward declaration in a header file (neither are present in your code snippet).
On last thing : in getLength(), you should check if the char * passed is valid by doing something like if (start){...}. Because if start == NULL, your program will crash since you will try to access a null pointer.
Following your edit : you can not separate an if() statement and the else if() logically following it. The else if() can be changed to if() here since in the case you enter the first if(), you quit the program (return 1;). And if you don't (if there is 3 arguments passed to the program), you can check safely if argv[1] and argv[2] have the same length.
Your main, slightly modified :
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Invalid number of items input.\n");
return 1;
}
int len1 = getLength(argv[1]);
int len2 = getLength(argv[2]);
if (len1 != len2) {
fprintf(stderr, "%s and %s are not the same size.\n", argv[1], argv[2]);
return 1;
}
else {
printf("%s", "Hello\n");
}
return 0;
}
Related
I'm trying to write a program that:
1- must have 2 arguments in the command line.
2- must call a function that checks if the 2nd argument is a digit.
This was my best try:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
bool only_digits(string s[1]);
int main(int argc, string argv[])
{
// Check how many arguments were typed in the command line.
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
// Check that argv[1] is a digit.
bool is_digit = only_digits(&argv[1]);
if (is_digit)
{
printf("Usage: ./caesar key\n./caesar ");
return 1;
}
return 0;
}
bool only_digits(string s[1])
{
for (int i = 0, n = strlen(s[1]); i < n; i++)
{
int is_digit = isdigit(s[1][i]);
if (is_digit == 0)
{
return 1;
}
}
return 0;
}
When I run this program and submit a string as the 2nd argument in the command line, the result is "Segmentation fault (core dumped)".
I get that the problem is that "s[1]" in my function doesn't include a NULL character at the end, so the function can't find the end of the string.
However, when I include the function in main, the problem disappears.
int main(int argc, string argv[])
{
// Check how many arguments were typed in the command line.
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
// Check that argv[1] is a digit.
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
int is_digit = isdigit(argv[1][i]);
if (is_digit == 0)
{
printf("Usage: ./caesar key\n./caesar ");
return 1;
}
}
return 0;
}
But as I need to do this calling a function, I need help :P
The function must be declared like
bool only_digits(string s);
and called like
bool is_digit = only_digits(argv[1]);
The function definition can look the following way
bool only_digits( string s )
{
if ( *s == '\0' ) return false;
while ( isdigit( ( unsigned char )*s ) ) ++s;
return *s == '\0';
}
Though it would be better to declare the function without changing its definition like
bool only_digits( const char *s );
I actually solved my problem a few minutes after I've posted...
I simply added:
bool only_digits(string s);
...
string argument = argv[1]; // <- this line!
bool is_digit = only_digits(argument);
And of course, I removed "[1]" from the function definition...
:)
I am making a program that will search in an array of strings, and for each string, it will search for a specified char. If it finds that char, remove it. In this example I want to remove the character 'r'.
Here is the code:
void convertStrings(char **line) {
for (int str = 0; str < MAX_LINE_LENGTH; ++str) {
for (int ch = 0; ch < MAX_STR_LENGTH; ++ch) {
if (line[str][ch] == 'r') {
removeChar(line[str], 'r');
}
}
}
}
void removeChar(char *str, char c) {
int i = 0;
int j = 0;
while (str[i]) {
if (str[i] != c) {
str[j++] = str[i];
}
i++;
}
str[j]=0;
}
I am not sure if the algorithm for the removal of chars is correct, however the main mistake is elsewhere. More specifically, I get a segmentation fault in the line:
if (line[str][ch] == 'r') {
Why am I getting a seg fault? Also, is the algorithm for removeChar correct?
Here is my main function:
int main() {
char line[3][10] = {"pep", "rol", "rak"};
printf("%s\n", line[1]);
convertStrings(line);
printf("%s\n", line[1]);
return 0;
}
Thanks in advance.
This code works on my compiler :
#include<stdio.h>
#include<conio.h>
#define MAX_LINE_LENGTH 1024
#define MAX_STR_LENGTH 4
void removeChar(char *str, char c) {
int i = 0;
int j = 0;
while (str[i]) {
if (str[i] != c) {
str[j++] = str[i];
}
i++;
}
str[j]=0;
}
void convertStrings(char line[][MAX_STR_LENGTH]) { //change 1
for (int str = 0; str < MAX_LINE_LENGTH; ++str) {
for (int ch = 0; ch < MAX_STR_LENGTH; ++ch) {
if (line[str][ch] == 'r') {
removeChar(line[str], 'r');
}
}
}
}
int main() {
char line[3][MAX_STR_LENGTH] = {"pep", "rol", "rak"}; //change 2
printf("%s\n", line[1]);
convertStrings(line);
printf("%s\n", line[1]);
getch();
return 0;
}
It's because line[str][ch] doesn't exist for all the value you give to str and/or ch.
You should check the value of MAX_LINE_LENGTH and MAX_STR_LENGTH and be sure that they are right.
The seg fault may be because you are using the constants "MAX_LINE_LENGTH" and "MAX_STR_LENGTH" however there may have the line length or string length. I would use the length of the array for the variable str in the first for loop instead of "MAX_LINE_LENGTH" and the length of array[str] instead of "MAX_STR_LENGTH". Unless each array you are searching has "MAX_LINE_LENGTH" and each string has "MAX_LINE_LENGTH" you will get a set fault. Hope this helps!
EDIT: you can find the length of the array by dividing the size of the array and the size of the type of the element.
sizeof(array)/sizeof(array[0])
finding the size of the char pointer is basically the same process.
You are getting a segfault either because array line contains fewer than MAX_LINE_LENGTH string pointers, or because at least one of the pointed-to strings contains fewer than MAX_STR_LENGTH characters; more likely the latter.
Instead of assuming a fixed number of strings of fixed length, you would be better off passing the actual number of strings as an argument. Alternatively, you could add NULL as sentinel value at the end of the list.
Moreover, there is no reason whatever to assume that each string is a fixed length. Look for the terminating character ('\0') to recognize when you've reached the end. For example:
void convertStrings(char **line) {
for (char **l = line; *l != NULL; l += 1) {
for (int ch = 0; (*l)[ch]; ch += 1) {
if ((*l)[ch] == 'r') {
removeChar(*l, 'r');
}
}
}
}
Your removeChar() function looks ok.
Do note, however, that there are library functions that could help with this (e.g. strchr()), and that there are various efficiency improvements possible (such as passing to removeChar() only the string tail, starting at the first appearance of the character to remove).
You have the array
char line[3][10] = {"pep", "rol", "rak"};
When you pass it to a function, it gets converted into a pointer of type char(*)[10]. So change
void convertStrings(char **line) {
to
void convertStrings(char (*line)[10]) {
or
void convertStrings(char line[][10]) {
An array of arrays (2D array) cannot be converted to a pointer to a pointer(in this case, char**)
Another problem is that you mention that MAX_LINE_LENGTH is 1024 and MAX_STR_LENGTH is 4. This is wrong as the loop would iterate and you access invalid memory locations. You should make MAX_LINE_LENGTH as 3 and MAX_STR_LENGTH as 4 as there are 3 strings, each with 4 characters.
You can also pass these variables as parameters to the function convertStrings. Change add two more parameters in the declartion of convertStrings:
void convertStrings(char (*line)[10], int MAX_LINE_LENGTH, int MAX_STR_LENGTH) {
or
void convertStrings(char line[][10], int MAX_LINE_LENGTH, int MAX_STR_LENGTH) {
and call the function from main using
convertStrings(line, sizeof(line)/sizeof(*line), sizeof(*line)/sizeof(**line)); // `/sizeof(**line)` is 1 and is not needed
A better way would be to use
void convertStrings(int MAX_LINE_LENGTH, int MAX_STR_LENGTH, char line[][MAX_STR_LENGTH]) {
or
void convertStrings(int MAX_LINE_LENGTH, int MAX_STR_LENGTH, char (*line)[MAX_STR_LENGTH]) {
and call the function using
convertStrings(sizeof(line)/sizeof(*line), sizeof(*line)/sizeof(**line), line); // `/sizeof(**line)` is 1 and is not needed
so that you can avoid using the magic number 10 in your function.
You would've certainly got some warnings from your compiler. Pay attention to them. If you did not get warnings, crank up the warnings in your compiler and include warning flags ( like -Wall in GCC ).
BTW, You can look into the strchr function from string.h to find if a character exists in a string.
Why do you check if you encounter the 'r' character twice? (in both function)
checking once would be enough.
A function to detect the char, and a function to delete it?
I would have done it this way :
#include <string.h>
#include <stdio.h>
void convertStrings(char *line);
void removeChar(char *str);
int main(int argc, char *argv[]) {
if (argc == 2)
{
printf("%s\n", argv[1]);
convertStrings(argv[1]);
printf("%s\n", argv[1]);
}
return (0);
}
void convertStrings(char *line)
{
for (int i = 0; line[i] != '\0'; i++)
{
if (line[i] == 'r') removeChar(&(line[i]));
}
}
void removeChar(char *str)
{
int i;
i = 0;
while (str[i] != '\0')
{
str[i] = str[i + 1];
i++;
}
}
But here is another one solution with only one function :
void convertStringsbis(char *line)
{
int delta;
int i;
i = 0;
delta = 0;
while (line[i++ + delta] != '\0')
{
if (line[i + delta] == 'r')
delta++;
line[i] = line[i + delta];
}
}
In this function I am going to be receiving char * words such as
person.vet.blah
and
word.friends.joe
I want to extract the first word. So for the first one I want to extract
person
and the second one I want to extract
word
How can I correctly do this? Here is my code:
char *separate_name(char *machine_name)
{
//iterate until you find period. then return
char absolute_name[1000];
int i;
for (i =0; i < strlen(machine_name); i++)
{
if (machine_name[i] == '.')
absolute_name[i] = machine_name[i];
}
return absolute_name;
}
This is just segfaulting. Any ideas what I should be doing? machine_name is going to be the "person.vet.blah" and then return absolute_name which would be "person"
Fixing your code
As others have pointed out, you can't use absolute_name outside of the function in which it was defined. This is because you're when you return the variable from your function, all that is being returned is a pointer to the beginning of the array. Outside the function, the array itself no longer exists, so the pointer is invalid and you get a segfault if you try and dereference it.
You can get around this by using malloc. Don't forget to free the memory you have allocated when you are done using it.
By the way, as well as changing your loop to a while, I also fixed the check (you were checking machine_name[i] == '.', the opposite to what you wanted).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *separate_name(char *machine_name)
{
// allocate memory on the heap
char *absolute_name = malloc(strlen(machine_name)+1);
int i = 0;
while (i < strlen(machine_name) && machine_name[i] != '.') {
absolute_name[i] = machine_name[i];
++i;
}
absolute_name[i] = '\0';
return absolute_name;
}
int main()
{
char name1[] = "person.vet.blah";
char *first1 = separate_name(name1);
if (first1 != NULL) {
printf("%s\n", first1);
free(first1);
}
char name2[] = "word.friends.joe";
char *first2 = separate_name(name2);
if (first2 != NULL) {
printf("%s\n", first2);
free(first2);
}
return 0;
}
A better alternative
strtok is the perfect tool for the job:
#include <stdio.h>
#include <string.h>
char *separate_name(char *machine_name)
{
return strtok(machine_name, ".");
}
int main()
{
char name1[] = "person.vet.blah";
char *first1 = separate_name(name1);
if (first1 != NULL) printf("%s\n", first1);
char name2[] = "word.friends.joe";
char *first2 = separate_name(name2);
if (first2 != NULL) printf("%s\n", first2);
return 0;
}
As pointed out in the comments (thanks #John), strtok modifies the string that is passed to it (it replaces the delimiter . by the \0 null byte to mark the end of the string). This isn't a problem here but is something to be aware of.
Output using either program:
person
word
#include <stdio.h>
char *separate_name(const char *machine_name){
static char absolute_name[1000];
int i;
for (i =0; i < sizeof(absolute_name)-1 ; i++){
if(machine_name[i] == '.' || machine_name[i] == '\0'){
absolute_name[i] = '\0';
break;
} else {
absolute_name[i] = machine_name[i];
}
}
return absolute_name;
}
int main(void){
printf("%s\n", separate_name("person.vet.blah"));
printf("%s\n", separate_name("word.friends.joe"));
return 0;
}
I have a program that reverses a string from an input of a variable length character array. The function returns a variable length character array and is printed. When I print the output, I do get the reversed string, but there are garbage characters appended to it in my console print.
Is this a "legal" operation in terms of returning to buffers? Can someone please critique my code and suggest a better alternative if it is not the right approach?
Thanks.
#include <stdio.h>
#include <stdlib.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
int i=0;
int j=0;
char *return_string;
char filled_buffer[16];
while (input_string[i]!='\0')
i++;
while (i!=0)
{
filled_buffer[j]=input_string[i-1];
i--;
j++;
}
return_string=filled_buffer;
printf("%s", return_string);
return return_string;
}
int main (void)
{
char *returned_string;
returned_string=reverse_string("tasdflkj");
printf("%s", returned_string);
return 1;
}
This is my output from Xcode - jklfdsat\347\322̲\227\377\231\235
No, it isn't safe to return a pointer to a local string in a function. C won't stop you doing it (though sometimes the compiler will warn you if you ask it to; in this case, the local variable return_string prevents it giving the warning unless you change the code to return filled_buffer;). But it is not safe. Basically, the space gets reused by other functions, and so they merrily trample on what was once a neatly formatted string.
Can you explain this comment in more detail — "No, it isn't safe..."
The local variables (as opposed to string constants) go out of scope when the function returns. Returning a pointer to an out-of-scope variable is undefined behaviour, which is something to be avoided at all costs. When you invoke undefined behaviour, anything can happen — including the program appearing to work — and there are no grounds for complaint, even if the program reformats your hard drive. Further, it is not guaranteed that the same thing will happen on different machines, or even with different versions of the same compiler on your current machine.
Either pass the output buffer to the function, or have the function use malloc() to allocate memory which can be returned to and freed by the calling function.
Pass output buffer to function
#include <stdio.h>
#include <string.h>
int reverse_string(char *input_string, char *buffer, size_t bufsiz);
int reverse_string(char *input_string, char *buffer, size_t bufsiz)
{
size_t j = 0;
size_t i = strlen(input_string);
if (i >= bufsiz)
return -1;
buffer[i] = '\0';
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", buffer);
return 0;
}
int main (void)
{
char buffer[16];
if (reverse_string("tasdflkj", buffer, sizeof(buffer)) == 0)
printf("%s\n", buffer);
return 0;
}
Memory allocation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
size_t j = 0;
size_t i = strlen(input_string) + 1;
char *string = malloc(i);
if (string != 0)
{
string[--i] = '\0';
while (i != 0)
{
string[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", string);
}
return string;
}
int main (void)
{
char *buffer = reverse_string("tasdflkj");
if (buffer != 0)
{
printf("%s\n", buffer);
free(buffer);
}
return 0;
}
Note that the sample code includes a newline at the end of each format string; it makes it easier to tell where the ends of the strings are.
This is an alternative main() which shows that the allocated memory returned is OK even after multiple calls to the reverse_string() function (which was modified to take a const char * instead of a plain char * argument, but was otherwise unchanged).
int main (void)
{
const char *strings[4] =
{
"tasdflkj",
"amanaplanacanalpanama",
"tajikistan",
"ablewasiereisawelba",
};
char *reverse[4];
for (int i = 0; i < 4; i++)
{
reverse[i] = reverse_string(strings[i]);
if (reverse[i] != 0)
printf("[%s] reversed [%s]\n", strings[i], reverse[i]);
}
for (int i = 0; i < 4; i++)
{
printf("Still valid: %s\n", reverse[i]);
free(reverse[i]);
}
return 0;
}
Also (as pwny pointed out in his answer before I added this note to mine), you need to make sure your string is null terminated. It still isn't safe to return a pointer to the local string, even though you might not immediately spot the problem with your sample code. This accounts for the garbage at the end of your output.
First, returning a pointer to a local like that isn't safe. The idiom is to receive a pointer to a large enough buffer as a parameter to the function and fill it with the result.
The garbage is probably because you're not null-terminating your result string. Make sure you append '\0' at the end.
EDIT: This is one way you could write your function using idiomatic C.
//buffer must be >= string_length + 1
void reverse_string(char *input_string, char* buffer, size_t string_length)
{
int i = string_length;
int j = 0;
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
buffer[j] = '\0'; //null-terminate the string
printf("%s", buffer);
}
Then, you call it somewhat like:
#define MAX_LENGTH 16
int main()
{
char* foo = "foo";
size_t length = strlen(foo);
char buffer[MAX_LENGTH];
if(length < MAX_LENGTH)
{
reverse_string(foo, buffer, length);
printf("%s", buffer);
}
else
{
printf("Error, string to reverse is too long");
}
}
My question is basically using printf print a char array.
In some cases, it prints the result out:
int main(int argc, char** argv) {
char* orig = "#reveals#?the treasure chest#$President Barack H. Obama#";
printf("The input: %s\n", orig);
printf("The output: %s\n", reArrange(orig));
return (EXIT_SUCCESS);
}
sometimes not:
int main(int argc, char** argv) {
char* orig = "#reveals#?the treasure chest#$President Barack H. Obama#";
printf("%s\n", reArrange(orig));
return (EXIT_SUCCESS);
}
Here is the complete code (the main function is included):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SUBJECT '$'
#define ACTION '#'
#define OBJECT '?'
#define END '#'
char* reArrange(char* orig) {
int origSize = strlen(orig);
char subject[origSize], action[origSize], object[origSize];
//int i;
//for(i = 0; i < origSize; i++) {
// subject[i] = ' ';
// action[i] = ' ';
// object[i] = ' ';
//}
int subjectIndex = 0, actionIndex = 0, objectIndex = 0;
int timesEndCharShowUp = 0;
char state;
int i;
for(i = 0; i < origSize; i++) {
char ch = orig[i];
if(ch == SUBJECT) {
state = SUBJECT;
}
else if(ch == ACTION) {
state = ACTION;
}
else if(ch == OBJECT) {
state = OBJECT;
}
else if(ch == END) {
if(timesEndCharShowUp == 3) {
break;
}
else {
timesEndCharShowUp++;
}
}
else {
if(state == SUBJECT) {
subject[subjectIndex++] = ch;
}
else if(state == ACTION) {
action[actionIndex++] = ch;
}
else if(state == OBJECT) {
object[objectIndex++] = ch;
}
}
}
subject[subjectIndex] = '\0';
action[actionIndex] = '\0';
object[objectIndex] = '\0';
char rearranged[origSize];
sprintf(rearranged, "%s %s %s.\0", subject, action, object);
//printf("%s\n", rearranged);
orig = rearranged;
return orig;
}
int main(int argc, char** argv) {
char* orig = "#reveals#?the treasure chest#$President Barack H. Obama#";
// printf("The input: %s\n", orig);
// printf("The output: %s\n", reArrange(orig));
printf("result: ");
printf("%s\n", reArrange(orig) );
//fflush(stdout);
return (EXIT_SUCCESS);
}
You are returning a pointer to memory that resides on the stack. rearranged is not available after the enclosing function (reArrange) returns and may contain garbage.
You may want to malloc rearranged or declare it globally.
Instead of only returning a char *, make reArrange() accept a buffer to which it can write the result. Now the caller must provide a suitable buffer to your function, and you have no problems with memory management anymore. You merely strncpy() arranged to the buffer. To be sure the buffer is large enough, the user should also provide the size of the buffer, in a 3rd argument:
char *rArrange(char *orig, char *result, int resultSize)
{
if (!result || resultSize == 0)
return NULL;
/* your code here */
strncpy(result, arranged, resultSize);
return result;
}
The alternative malloc() to store the result is not very user fiendly (the user must freemem() the buffer but may not be aware of this). Using a static/global buffer is not very thread-safe.
Your function needs two parameters:
char* reArrange(char* orig)
Should be:
char* reArrange(char *rearragned, char* orig) {
// make your magic here!
}
Calling sequence:
char input[SIZE];
char rearrange [SIZE];
// initialize everything! Don't forget to rearrange[0] ='\0\;!!!
rearrange(rearranged, input);
// do you printing....
You should also learn how to use pointers correctly and look up "switch".
The problem is that your function reArrange returns a pointer to memory that it no longer controls. The address of the rearranged array is returned. After the return occurs, this array effectively no longer exists -- the memory can, and will, be reused.
A quick hack which fixes your mistake is to declare rearranged as static. The long term fix is to learn how C works and code something using malloc() or an equivalent.
With char rearranged[origSize]; you created a new character array which goes out of scope once reArrange terminates. So during the lifetime of reArrange, rearranged is a pointer which points to something meaningful ; hence, orig = rearranged makes sense.
But once it goes out of scope, reArrange(orig) returns the pointer to rearranged which is a dangling pointer now rearranged does not exist anymore.
Are you sure that the following section of the code works?
char* reArrange(char* orig) {
int origSize = strlen(orig);
char subject[origSize], action[origSize], object[origSize];
i mean origSize has to be a const it can't be a dynamic value. You should use malloc for allocating apace for subject , action and object.
Moreover, you might consider a few guidelines:
1 Instead of:
for(i = 0; i < origSize; i++) {
char ch = orig[i];
if(ch == SUBJECT) {
state = SUBJECT;
}
You can have:
char ch;//declare char ch outside for loop
for(i = 0; i < origSize; i++) {
ch = orig[i];
if(ch == SUBJECT) {
state = SUBJECT;
}
2 you may like to use a switch statement instead of if, that will make your code look great.