I keep getting a segmentation fault on this one, does someone see the reason?
void str_sort(char s[]) {
int i, j;
char c;
for (i = strlen(s) - 1; i > 0; i--)
for (j = 0; j < i; j++)
if (*(s+j) > *(s+j+1)) {
c = *(s+j);
*(s+j) = *(s+j+1);
*(s+j+1) = c;
}
return;
}
String literals are stored in a region of memory that disallows modifications. If you create an array to store the string instead, it should work fine (assuming your function is correct which it looked like at first glance):
int main()
{
char str[] = "PC-spiel";
str_sort(str);
return 0;
}
The difference is a string literal like "PC-spiel" is a pointer to a non-modifiable constant. But the string str is located on the stack like a normal array/variable and gets the value "PC-spiel" copied into it at runtime.
Your string sorting function operates in place. It will modify its string argument. It you call it with a string literal, trying to modify the argument will invoke undefined behavior. If the string literal is already sorted such as "abc", no problem since no write access is performed, but if you call str_sort("ba"); undefined behavior ensues and a crash is a possible form of undefined behavior.
Note that you should use the index notation for better readability and the final return; is useless:
void str_sort(char s[]) {
int i, j;
char c;
for (i = strlen(s) - 1; i > 0; i--) {
for (j = 0; j < i; j++) {
if (s[j] > s[j+1]) {
c = s[j];
s[j] = s[j+1];
s[j+1] = c;
}
}
}
}
Related
I was solving the question of leet code in C
Question:
Given a string s, sort it in decreasing order based on the frequency of the characters. The frequency of a character is the number of times it appears in the string.
Return the sorted string. If there are multiple answers, return any of them.
Example 1:
Input: s = "tree"
Output: "eert"
Explanation: 'e' appears twice while 'r' and 't' both appear once.
So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
I tried to use the different approach instead taking the array[255] and increasing the array value in specific char ASCII index. But I m getting segmentation fault. I not understand why I m getting segmentation voilation. Only assumption I made here is input str always be in UPPER CASE.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *frequencySort(char *s)
{
int strlenn = strlen(s);
int fq[strlenn];
// init with 1 because at least 1 time char occur in input str.
for (int i = 0; i < strlenn; i++)
{
fq[i] = 1;
}
// count freq of string and replace dublicate char with *
// ex: ABCDA => ABCD*
for (int i = 0; i < strlenn - 1; i++)
{
if (s[i] == '*')
{
continue;
}
for (int j = (i + 1); j < strlenn; j++)
{
if (s[i] == s[j])
{
fq[i]++;
s[j] = (char)'*'; // segmentation violation error shows here
fq[j] = 0;
}
}
}
// sort freqency by in decending order and str char
// ex: ABCDDDAA = freq[3, 3, 1, 1] = soredt str = [ A, D, B, C ]
for (int i = 0; i < strlenn - 1; i++)
{
for (int j = i + 1; j < strlenn; j++)
{
if (fq[i] < fq[j])
{
// swap
int temp = fq[i];
fq[i] = fq[j];
fq[j] = temp;
// swap string char
char temp1 = s[i];
s[i] = s[j]; // segmentation violation error shows here
s[j] = temp1;
}
}
}
char *result = (char *)calloc(strlenn + 1, sizeof(char));
int l = 0;
for (int i = 0; fq[i] != 0 && i < strlenn; i++)
{
int k = fq[i];
while (k > 0)
{
printf("%c", s[i]);
result[l++] = s[i];
k--;
}
}
result[l] = '\0';
return result;
}
int main()
{
char *s = "ABCDDDAA";
frequencySort(s);
return 0;
}
Thanks!
In frequencySort, you are modifying s (e.g.):
s[j] = (char)'*'; // segmentation violation error shows here
In main, the s argument comes from:
char *s = "ABCDDDAA";
frequencySort(s);
Here s is function scoped and goes on the stack.
But, because this is a pointer to a string literal, the actual string data goes into the .rodata section. This is mapped as read only. So, when we try to change it, we get a protection exception.
To fix this, define a string array:
char s[] = "ABCDDDAA";
frequencySort(s);
Now the literal data is [still] in the .rodata section. But, when main starts up, it is copied into the s array which is on the stack [which is writable].
I'm trying to organize a string but my code fails. I'm not sure what I'm doing wrong, or even if my code is close to what I'm trying to do.
void ordenar(int lengh, char string[])
{
char aux[20];
for (int i = 0; i < lengh; i++) {
for (int j = 0; j < lengh; j++){
if (strcmp(string[j], string[i] > 0)) {
strcpy(aux, string[i]);
strcpy(string[i], string[j]);
strcpy(string[j], aux);
}
}
}
}
int main()
{
char nombre_prueba[10] = "Leandro";
ordenar(7, nombre_prueba);
for (int i = 0; i < 7; i++) {
printf("%s",nombre_prueba);
}
return 0;
}
I asked a friend to help, but he has the same problem.
Your compiler should be issuing some pretty serious diagnostic messages, that you should not be ignoring. If it is quiet, you need need to turn up your compiler warning level:
For gcc or clang, use -Wall -Wextra
For MSVC, use /Wall
The strcmp function takes two strings as arguments, as const char *. In your code, string[j] and string[i] are both of type char, an integral value representing a single character in the string.
Even if you had two strings to compare, the > 0 is within the argument list of the function call, which becomes much more clear with better formatting.
strcmp(string[j], string[i] > 0)
This function is not needed to reorder the contents of a string. With an ASCII encoding, you could compare each character directly.
Note that the printf specifier %s prints a single string. This call is correct, printf("%s", nombre_prueba);, but you are repeating it seven times.
With minimal changes, your code could look like:
#include <stdio.h>
void ordenar(int lengh, char string[])
{
for (int i = 0; i < lengh; i++) {
for (int j = 0; j < lengh; j++) {
if (string[j] > string[i]) {
char t = string[i];
string[i] = string[j];
string[j] = t;
}
}
}
}
int main(void)
{
char nombre_prueba[10] = "Leandro";
ordenar(7, nombre_prueba);
printf("%s\n", nombre_prueba);
}
which prints Ladenor. Note that uppercase letters come before lowercase letters in the ASCII table. This creates a string in ASCIIbetical order.
The task was mainly to use pointers to input a string and slice it at places where there is a '\' character and output them in separate lines, using pointers. The program runs fine when I use arrays instead of pointers. However using pointers to store strings give the message "Segmentation fault". The code is as follows :
#include <stdio.h>
#include <stdlib.h>
int main() {
char *name;
char *sep[100];
int i = 0, j = 0, k = 0;
scanf("%[^\n]s", name);
for(i = 0; (*(name+i)) != '\0'; i++) {
if((*(name+i)) == '\\') {
*((*(sep+k))+j) = '\0';
j = 0;
k++;
} else {
*((*(sep+k))+j) = *(name+i);
j++;
}
}
for(i = 0; i <= k; i++) {
printf("%s\n", *(sep+i));
}
return 0;
}
It would be awesome if you could point out what and where the problem is, instead of giving me an alternative solution. TIA.
your pointers are null pointers.you are invoking undefined behavior by using them without assigning them to allocated memory.Allocate memory to them so that you can use them correctly and store words separated by \.Also,you can use [] instead of *.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[256];
char *sep[100];
for( int n = 0 ; n < 100 ; n++ )
{
sep[n] = malloc(30*sizeof(char));
}
int i = 0, j = 0, k = 0;
scanf(" %255[^\n]s", name);
for(i = 0; name[i] != '\0'; i++)
{
if( name[i] == '\\')
{
sep[k][j] = '\0';
j = 0;
k++;
}
else
{
sep[k][j] = name[i];
j++;
}
}
sep[k][j] = '\0';
for(i = 0; i <= k ; i++)
{
printf("%s\n",sep[i]);
}
for( int n = 0 ; n < 100 ; n++ )
{
free(sep[n]);
}
return 0;
}
In your code,
scanf("%[^\n]s", name);
name is an unintialized pointer. It does not point to any valid memory location. You need to allocate memory before you can use it.
The same goes out for sep array, too.
You can consider using an array for this purpose or see the man page of malloc() if you want to stick to a pointer.
FWIW, using an unitialized pointer can lead to undefined behavior.
You must allocate space for your pointers to avoid undefined behaviour: you cannot use a pointer without initializing it.
int main() {
char *name = malloc(MAX_DIM_OF_NAME+1);
char *sep[100];
for (int i=0; i<100; i++)
sep[i] = malloc(MAX_DIM_OF_NAME+1);
....
You call scanf with an uninitialized name.
I receive the error in the title when I try to run this code:
//Copies the information from one array to another until a set length using pointers
void strncpy2(char* t, const char* s, const unsigned int n)
{
unsigned int i;
for (i = 0; i < n /* This i < n causes the error */ && *t++ = *s++; i++);
*(t + i) = '\0';
}
When I take out the i < n it works fine, and when I move the i < n to the right of the *t++ = *s++ I get no compiling error, however the code doesn't work as intended.
What's going on here? I appreciate any help. Thank you!
You need parentheses around the assignment in the loop, since the && operator has higher precedence than the assignment. Try this:
for (i = 0; i < n && (*t++ = *s++); i++);
The semantic of your call is not clear, especially it differs significantly from strncpy().
You seem to manipulate raw string but you don't check for the end of string, which is unsafe.
Moreover the assignment after the loop uses "t" as a base pointer, which is wrong since you modified it.
I don't know what "n" implies on the size of t, if t has to be at least size n, you have to stop iterating at n-2 or your final assignment is out-of-bound.
Finally I stay away from codes that have side effects in the for loop header.
void strncpy2(char* t, const char* s, const unsigned int n)
{
unsigned int i;
for (i = 0; i < n-1; i++) {
t[i] = s[i];
if(s[i] == '\0') return;
}
t[i] = '\0';
}
if you want to use pointers chasing (c):
void strncpy2(char* t, const char* s, const unsigned int n)
{
unsigned int i;
for (i = 0; i < n-1; i++) {
*t = *s;
if(*s == '\0') return;
t++; s++;
}
*t = '\0';
}
Now I don't know what is the point of doing that, already having strncpy() and memcpy() in the standard library. And of course, C++ code should avoid raw string ;)
I am trying to reverse a character string in C
Here is what I have
void reverse(char str[]) {
int i = 0;
int length;
// Get string length
for (i = 0; str[i] != '\0' ; ++i) {
length = i;
}
char reversed[1000];
int j;
j = 0;
// Reverse it
for (j = 0; j < length ; ++j) {
reversed[j] = str[length - j];
}
}
I know that reversed contains the string reversed, but I'm not sure how to modify the original str without throwing away data I need.
I also don't know how to set strto reversed without looping again.
Would it be acceptable to do another...
int m;
m = 0;
for (m = 0; m < length ; ++m) {
str[j] = reversed[j];
}
Usually I'd say this many loops smells, but I'm also still quite unfamiliar with the language so I'm not sure...
Update
Thanks for all the answers guys, and I appreciate the edits too!
I ended up going with this...
int main() {
char str[] = "Reverse me!";
int length;
for (length = 0; str[length] != '\0'; length++) {
}
printf("length => %d chars\n", length);
int j, k;
char c;
for (j = 0, k = length - 1; j < k; j++, k--) {
c = str[k];
str[k] = str[j];
str[j] = c;
}
printf("reversed => %s\n", str);
return 0;
}
Some things I now know...
There is a strlen() like in PHP. However, it has not been discussed in the book yet, plus I need to get familiar with null terminated strings.
A for loop can assign and do multiple things that are comma separated. I never knew this!
So asking was worthwhile :)
You want to do an in-place reversal. Here's a standard algorithm:
// taken from The C Programming Language
// by Brian Kernighan and Dennis Ritchie (K&R)
void reverse(char s[])
{
int c, i, j;
for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
Note that strlen pretty much replaces your original first loop. It's one of the many standard string manipulation routines available from string.h.
See also
Wikipedia/In-place algorithm
Wikipedia/string.h
Wikipedia/C Standard Library
You can use either of the two methods below, depending on whether you're comfortable with pointers or not. It will also be worthwhile looking at them side-by-side when you satrt learning about pointers so you can better understand how they map to each other.
This is a full program for testing purposes:
#include <stdio.h>
#include <string.h>
// The pointer version.
void reverse1 (char *str) {
char t; // Temporary char for swapping.
char *s = str; // First character of string.
char *e = &(s[strlen(s)-1]); // Last character of string.
// Swap first and last character the move both pointers
// towards each other. Stop when they meet or cross.
while (s < e) {
t = *s;
*s++ = *e;
*e-- = t;
}
}
// The array version.
void reverse2 (char *str) {
char t; // Temporary char for swapping.
int s = 0; // First character of string.
int e = strlen(str)-1; // Last character of string.
// Swap first and last character the move both pointers
// towards each other. Stop when they meet or cross.
while (s < e) {
t = str[s];
str[s++] = str[e];
str[e--] = t;
}
}
int main (void) {
char x[] = "This is a string for reversing.";
printf ("Original: [%s]\n", x);
reverse1 (x);
printf ("Reversed: [%s]\n", x);
reverse2 (x);
printf (" Again: [%s]\n", x);
return 0;
}
and the output is:
Original: [This is a string for reversing.]
Reversed: [.gnisrever rof gnirts a si sihT]
Again: [This is a string for reversing.]
Comments on your code:
void reverse(char str[]) {
int i = 0;
int length;
// Get string length
for (i = 0; str[i] != '\0' ; ++i) {
length = i;
}
Rather than copying the i to length every time you could just wait until the end.
size_t len = 0; // size_t is an unsigned integer that is large enough to hold the sizes
// of the biggest things you can have (32 bits on 32 bit computer,
// 64 bits on a 64 bit computer)
char * s = str;
while (*s) {
len++;
s++;
}
Though the compiler would probably be able to make this optimization for you.
You should know, though, that there is a standard string function strlen ( #include <string.h> )which will measure the length of a char string using the same general algorithm (look for the end) but is usually optimized for the target processor.
len = strlen(str);
Your code again:
char reversed[1000];
Using big arrays are good for learning and simple examples, but you can also allocate memory dynamically based on the size you now know you need. The standard function for doing this is malloc which is in stdlib.h (also in malloc.h). Memory allocated with this function should also be freed, though.
int * p = malloc( 8 * sizeof(int) ); // allocate an array of 8 ints
/* ... work on p ... */
free(p);
/* ... don't access the memory pointed to by p anymore ... */
p = 0;
There are also other functions in the malloc family. There's calloc, which allocates memory and clears sets it to 0. There is also a function called strdup (which isn't in standard C, but is very widely available in string.h) which takes a string and allocates a duplicate of it. It is really just:
char * strdup(const char * str) {
size_t len = strlen(str);
char * s = malloc(len+1);
if (!s) {
return s;
}
return strcpy(s,str); // This could have been memcpy since you know the size
// and memcpy might have been faster on many processors
}
Another useful memory allocation function is alloca (not in the C standard, but widely available and similar functionality is available with variable length arrays in C99). It is great, but works differently from malloc. It allocates memory that is only useable until the current function returns because this memory is allocated in the same way as memory for local variables (from the stack).
More of your code:
int j;
j = 0;
// Reverse it
for (j = 0; j < length ; ++j) {
reversed[j] = str[length - j];
}
The code:
void reverse_in_place(char * str, size_t len) {
size_t i, j;
for (i = 0, j = len - 1; i < j ; i++, j--) {
char a = str[i];
char z = str[j];
str[i] = z;
str[j] = a;
}
}
should swap the order of the string without making a copy of it. For strings with odd length it won't try to swap the middle char with itself.
Reversing a string in C using pointers
#include<stdio.h>
char *srcptr = "Hello!";
char *destptr;
unsigned int length = 0;
void main(void)
{
while(*(ptr++) != '\0')
{
length++;
}
//at the end of while loop, pointer points to end of string
while(length--)
{
*destptr++ = *ptr--;
}
//append null at the end
*destptr = '\0';
printf("%s",ptr);
}